最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - If all conditions matches inside foreach - Stack Overflow

programmeradmin6浏览0评论

I have this type of html:

<p id="username_input" data-priority=""> 
    <label for="username">Username</label>
    <input type="text" class="input-text um-field " name="username" id="username" placeholder="" value="" required="required" data-label="Username">
</p>

<p id="firstname_input" data-priority=""> 
     <label for="firstname">First Name</label>
     <input type="text" class="input-text um-field " name="firstname" id="firstname" placeholder="" value="" required="required" data-label="FirstName">
</p>
<p class="form-row " id="radio_SXsPOwVSD_input"> 
   <input type="radio" class="input-radio um-field" value="Male" name="radio_SXsPOwVSD" id="radio_SXsPOwVSD_male">Male 
   <input type="radio" class="input-radio um-field" value="Female" name="radio_SXsPOwVSD" id="radio_SXsPOwVSD_Female">Female 
</p>

applyConditions array contains input, condition and value indexes. Could be any inputs and also many conditions. Suppose,

input = username
condition = 0 (is)
value = abc

input = firstname
condition = 1 (is not)
value = pqr

I need to do something(show/hide checkbox) if

username is abc and firstname isnot pqr

from the frontend. But could be input radio_sXsPOwVSD, condition 1 and value Male.

then,

applyConditions.forEach( function( item ) {

  if(item.condition == 0) {
    jQuery("#"+ item.input+"_input").on('change', ( function(  avalue ) {

        return function(e) {                
          if( e.target.value == avalue ) {
                //show checkbox
          }
          else {
                //hide checkbox
          }
        };
    })(item.value));
  }
  else if( item.condition == 1 ) {
        jQuery("#"+ item.input+"_input").on('change', ( function(  avalue ) {

        return function(e) {                
          if( e.target.value != avalue ) {
                //show checkbox
          }
          else {
           //hide checkbox
          }
        };
     })(item.value));
 }

});

However, this seems to be OR, if any matches, but I need all matches. I could count the matches and compare with array length, but with onChange count on same field seems to increase/decrease multiple times. What could be the solution? I am stuck on this for a while.

applyConditions = 
[
  {"input":"username","condition":"0","value":"abc"}, 
  {"input":"firstname","condition":"1","value":"pqr"}
];

could also be {"input":"radio_SXsPOwVSD","condition":"0","value":"Male"},

I have this type of html:

<p id="username_input" data-priority=""> 
    <label for="username">Username</label>
    <input type="text" class="input-text um-field " name="username" id="username" placeholder="" value="" required="required" data-label="Username">
</p>

<p id="firstname_input" data-priority=""> 
     <label for="firstname">First Name</label>
     <input type="text" class="input-text um-field " name="firstname" id="firstname" placeholder="" value="" required="required" data-label="FirstName">
</p>
<p class="form-row " id="radio_SXsPOwVSD_input"> 
   <input type="radio" class="input-radio um-field" value="Male" name="radio_SXsPOwVSD" id="radio_SXsPOwVSD_male">Male 
   <input type="radio" class="input-radio um-field" value="Female" name="radio_SXsPOwVSD" id="radio_SXsPOwVSD_Female">Female 
</p>

applyConditions array contains input, condition and value indexes. Could be any inputs and also many conditions. Suppose,

input = username
condition = 0 (is)
value = abc

input = firstname
condition = 1 (is not)
value = pqr

I need to do something(show/hide checkbox) if

username is abc and firstname isnot pqr

from the frontend. But could be input radio_sXsPOwVSD, condition 1 and value Male.

then,

applyConditions.forEach( function( item ) {

  if(item.condition == 0) {
    jQuery("#"+ item.input+"_input").on('change', ( function(  avalue ) {

        return function(e) {                
          if( e.target.value == avalue ) {
                //show checkbox
          }
          else {
                //hide checkbox
          }
        };
    })(item.value));
  }
  else if( item.condition == 1 ) {
        jQuery("#"+ item.input+"_input").on('change', ( function(  avalue ) {

        return function(e) {                
          if( e.target.value != avalue ) {
                //show checkbox
          }
          else {
           //hide checkbox
          }
        };
     })(item.value));
 }

});

However, this seems to be OR, if any matches, but I need all matches. I could count the matches and compare with array length, but with onChange count on same field seems to increase/decrease multiple times. What could be the solution? I am stuck on this for a while.

applyConditions = 
[
  {"input":"username","condition":"0","value":"abc"}, 
  {"input":"firstname","condition":"1","value":"pqr"}
];

could also be {"input":"radio_SXsPOwVSD","condition":"0","value":"Male"},

Share Improve this question edited Apr 2, 2018 at 12:52 micky asked Mar 31, 2018 at 12:16 mickymicky 3171 gold badge15 silver badges43 bronze badges 5
  • It would help if you gave us a broader view of what you are trying to do here. If it is a form of dynamic form validation on change of any of the fields, you'd better have the foreach loop with the eventhandler. But I am not sure if that is what you want. – Roemer Commented Mar 31, 2018 at 12:23
  • in condition 0, the selector within "_input", but in condition 1 it's "_field"? – Hikarunomemory Commented Mar 31, 2018 at 12:28
  • @Hikarunmemory oh it's input on both. I decided it to change later. – micky Commented Mar 31, 2018 at 13:20
  • @Roemer, the form is static but the conditions and fields to apply condition may vary. – micky Commented Mar 31, 2018 at 13:23
  • 1 That is what I meant. I would do a complete testing loop within the eventhandler, and assign that same eventhandler to all the elements. It is easy to see why: you cannot do an "AND" your way, since you test only one element per time. You need to test them all at once. – Roemer Commented Mar 31, 2018 at 13:34
Add a comment  | 

4 Answers 4

Reset to default 13

Compare if ($(ele).val() == item.value) equals item.condition

to determine whether it matches the condition.

The following version now works on radio buttons and checkbox.

Well, I finally found that there're name attr on every input.

var applyConditions = [
    {
        'input': 'username',
        'condition': 0,
        'value': 'abc'
    },
    {
        'input': 'firstname',
        'condition': 1,
        'value': 'pqr'
    },
    {
        "input": "radio_SXsPOwVSD",
        "condition": 0,
        "value":"Male"
    }, 
    {
        "input": "check_box_XmNoe",
        "condition": 0,
        "value": "Apple"
    }
]

applyConditions.forEach(function(condition) {
    var targetInput = $('input[name='+condition.input+']');
    targetInput.on('change',function(){
        var results = $.map(applyConditions,(item, index)=>{
            var targetInput = $('input[name='+item.input+']');
            var type = targetInput.get(0).type;
            var check = false;
            if(type == "radio" || type == "checkbox"){
                var input = targetInput.get().find(x=>$(x).is(":checked") && $(x).val() == item.value)
                if (input)
                {
                    check = input.value == item.value != item.condition;
                }
                else{
                    // since 0 means equal, if there's no any radio button or checkbox is checked, check = false
                    check = item.condition ? true : false;
                }
            }
            else{
                check = (targetInput.val() == item.value) != item.condition;
            }

            if(check){
                // matches
            }
            else{
                // not matches
            }
            return check;
        })

        console.log(...results);
    })
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p id="username_input" data-priority=""> 
    <label for="username">Username</label>
    <input type="text" class="input-text um-field" name="username" id="username" placeholder="" value="" required="required" data-label="Username">
    ( ==abc )
</p>

<p id="email_input" data-priority=""> 
    <label for="username">Username</label>
    <input type="text" class="input-text um-field" name="email" id="email" placeholder="" value="" required="required" data-label="Email">
    ( no condition )
</p>

<p id="firstname_input" data-priority=""> 
     <label for="firstname">First Name</label>
     <input type="text" class="input-text um-field" name="firstname" id="firstname" placeholder="" value="" required="required" data-label="FirstName">
     ( !=pqr )
</p>
<p class="form-row" id="radio_SXsPOwVSD_input"> 
   <input type="radio" class="input-radio um-field" value="Male" name="radio_SXsPOwVSD" id="radio_SXsPOwVSD_male">Male 
   <input type="radio" class="input-radio um-field" value="Female" name="radio_SXsPOwVSD" id="radio_SXsPOwVSD_Female">Female 
   ( Male should be checked )
</p>

<p id="check_box_XmNoe_input">
    <label class="checkbox data-label=">Checkbox</label>
    <input type="checkbox" class="input-checkbox um-field" name="check_box_XmNoe" id="check_box_XmNoe_Apple" value="Apple"> Apple
    <input type="checkbox" class="input-checkbox um-field" name="check_box_XmNoe" id="check_box_XmNoe_Orange" value="Orange"> Orange 
    ( Apple should be checked )
</p>

This is just an alternative algorithm.

var applyConditions = [
  {
    "input": "username",
    "condition": "0",
    "value": "abc",
  },
  {
    "input": "firstname",
    "condition": "1",
    "value": "pqr",
  },
  {
    "input": "radio_SXsPOwVSD",
    "condition": "0",
    "value": "Male"
  },
];
var totalConditions = applyConditions.length,
  conditionFulfilled = 0;

function compare($element, rule) {
  if ($element.length > 1) $element = $element.filter(':checked'); //radio
  return ($element.val() == rule.value) == (rule.condition == "0");
}

function setOK() {
  if (conditionFulfilled == totalConditions) $('#iresult').html('OK');
  else $('#iresult').html('Completed('+conditionFulfilled+'/'+totalConditions+')');
}

applyConditions.forEach(function(rule) {
  var $element = $('[name=' + rule.input + ']');
  var isConditionTrue = compare($element, rule);
  if (isConditionTrue) conditionFulfilled++;
  $element.change(function() {
    var updatedIsConditionTrue = compare($element, rule);
    if (isConditionTrue != updatedIsConditionTrue) {
      if (updatedIsConditionTrue) conditionFulfilled++;
      else conditionFulfilled--;
      isConditionTrue = updatedIsConditionTrue;
      setOK();
    }
  });
});
setOK();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p id="username_input" data-priority="">
  <label for="username">Username</label>
  <input type="text" class="input-text um-field " name="username" id="username" placeholder="value = abc" value="" required="required" data-label="Username">
</p>

<p id="firstname_input" data-priority="">
  <label for="firstname">First Name</label>
  <input type="text" class="input-text um-field " name="firstname" id="firstname" placeholder="value != pqr" value="" required="required" data-label="FirstName">
</p>
<p class="form-row " id="radio_SXsPOwVSD_input">
  <input type="radio" class="input-radio um-field" value="Male" name="radio_SXsPOwVSD" id="radio_SXsPOwVSD_male">Male
  <input type="radio" class="input-radio um-field" value="Female" name="radio_SXsPOwVSD" id="radio_SXsPOwVSD_Female">Female
</p>
<div>Result: <b id="iresult">Not OK</b></div>

Removed the Array and used the data attribute instead.

  
        $(document).ready(function(){
       
            $('.validator').delegate('input', 'change', function(event){
                event.stopPropagation();
                var condition = $(this).closest('.validator').data('condition');
                var valueToCompare = $(this).closest('.validator').data('value');
                var result = "";
                if(condition  == "1"){
                    result = $(this).val().trim() == valueToCompare ? "Condition Satisfied" : "Condition Not Satisfied";
                }else{
                    result = $(this).val().trim() != valueToCompare ? "Condition Satisfied" : "Condition Not Satisfied";
                }
                console.log(result)
            });
        });
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="validator" data-condition="1" data-value="Rony">
                <label for="username">First Name</label>
                <input type="text" >
        </div>
        <div class="validator" data-condition="0" data-value="White">
                <label for="username">Last name</label>
                <input type="text" >
        </div>
        <div class="validator" data-condition="1" data-value="Male">  
                <input type="radio" name="gender" value="Male">Male
                <input type="radio" name="gender" value="Female">Female
        </div>
        <div class="validator" data-condition="0" data-value="Banana">  
                <input type="radio" name="fruit" value="Apple">Apple
                <input type="radio" name="fruit" value="Banana">Banana
        </div>

To test that every one of a set of conditions is true use Array.prototype.every.

This solution has a function, setRules, which accepts a set of rules in the above format and returns an object with an onChange property to add listeners. These listeners are triggered when any of the values changes and are passed the new boolean value. It would not be hard to keep a current value so that this only fires when it switches from true to false or vice-versa.

You can use it like this:

const ruleHandler = addRules(applyConditions);
ruleHandler.onChange(function(newValue) { /* do something */ }

(or simply chain these together like below.)

This also makes it easy to add new conditions, as they are simple functions that accept the current value and the entire rule (enhanced with a jQuery reference to the element.) You can add a condition like this:

'2': function(val, rule) {return val.length >= rule.minLength;}

And use it like this:

{input: "lastname", condition: "2", minLength: "4"},

This also points out that you could use arbitrary names for these conditions, rather than "0" and "1". Perhaps they could be eq, not_eq, and if you use that last one, minLength.

Most of this is reasonably simple. The only part that I think needs explanation is this:

var newVal = rules.every(function(rule) {
  var $elt = (rule.$elt.length > 1) ? rule.$elt.filter(':checked') : rule.$elt;
  return (conds[rule.condition] || function() {return true;})($elt.val(), rule);
})

Note first that we start by calling every on our rules.

In the second line we let radio buttons be represented by the :checked element and others by the only element in the $elt object.

The third line is the heart of this code: it finds the correct condition function by looking it up, or if it's not found, chooses a function that simply returns true. Then it calls this function with the value of the element and the rule itself.

var setRules = function(rules) {
  var listeners = [];
  var conds = {
    '0': function(val, rule) {return val === rule.value;},
    '1': function(val, rule) {return val !== rule.value;},
  }
  var test = function() {
    var newVal = rules.every(function(rule) {
      var $elt = (rule.$elt.length > 1) ? rule.$elt.filter(':checked') : rule.$elt;
      return (conds[rule.condition] || function() {return true;})($elt.val(), rule);
    })
    listeners.forEach(listener => listener(newVal));
  }
  rules.forEach(function(rule) {
    rule.$elt = $('[name=' + rule.input + ']').change(test);
  });
  return {
    onChange: function(listener) {
      listeners.push(listener); 
      test(); 
    }
  };
}

var applyConditions = [
  {input: "username", condition: "0", value: "abc"},
  {input: "firstname", condition: "1", value: "pqr"},
  {input: "radio_SXsPOwVSD", condition: "0", value: "Male"}
];

const $results = $('#result');

setRules(applyConditions).onChange(function(newVal) {
  $results.html(newVal ? 'OK' : 'Not OK');
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<p id="username_input" data-priority="">
  <label for="username">Username</label>
  <input type="text" name="username" id="username" value="abc">
</p>

<p id="firstname_input" data-priority="">
  <label for="firstname">First Name</label>
  <input type="text" name="firstname" id="firstname" value="pqr">
</p>

<p id="radio_SXsPOwVSD_input">
  <input type="radio" value="Male" name="radio_SXsPOwVSD" id="radio_SXsPOwVSD_male" checked>Male
  <input type="radio" value="Female" name="radio_SXsPOwVSD" id="radio_SXsPOwVSD_Female">Female
</p>

<div>Result: <b id="result">Not OK</b></div>

Also note that this would be cleaner and more readable if you were using ES6:

const setRules = (rules) => {
  const listeners = [];
  const conds = {
    '0': (val, rule) => val === rule.value,
    '1': (val, rule) => val !== rule.value
  }
  const test = () => {
    var newVal = rules.every(function(rule) {
      var $elt = (rule.$elt.length > 1) ? rule.$elt.filter(':checked') : rule.$elt;
      return (conds[rule.condition] || (() => true))($elt.val(), rule);
    })
    listeners.forEach(listener => listener(newVal));
  }
  rules.forEach((rule) => {
    rule.$elt = $('[name=' + rule.input + ']').change(test);
  });
  return {
    onChange: (listener) => {
      listeners.push(listener); 
      test(); 
    }
  };
}   
发布评论

评论列表(0)

  1. 暂无评论