简体   繁体   中英

Javascript closure issue on click event

I have looked through the many answers to the "closure" issue but cannot get any to work on my specific issue.

The following js code takes a json file and stores it and then does some form validation based on the data.

The problem is on submission of the form and execution of the validate function I should see two errors but I only get the error for the last field (logged in the console).

This is an obvious closure problem but having spent all day on it I still can't fix it. Below is the code, the click event is at the bottom...

I am only checking the min length rule at this moment.

// Get the json file and store
function loadJSON(callback) {
  var xobj = new XMLHttpRequest();
  xobj.overrideMimeType("application/json");
  xobj.open('GET', 'js/rules.json');
  xobj.onreadystatechange = function () {
    if (xobj.readyState == 4 && xobj.status == "200") {
      // Required use of an anonymous callback as .open will NOT return a value but simply returns undefined in asynchronous mode
      callback(xobj.responseText);
    }
  };
  xobj.send(null);
}

// Load json...
loadJSON(response);

// Create global vars...
var lookup = [], errors = [], i, e, id, lookupId, minLength;

function response(responseData) {
  // Create objects from json data
  var rulesSet = JSON.parse(responseData);

  // Loop through objects
  for (i = 0;  i < rulesSet.length; i++) {
    // Create a lookup for each object that can be used later
    lookup[rulesSet[i].id] = rulesSet[i];
  }

  // Loop through form elements and store id's

  // Validate the form
  function validate(e) {
    var elements = document.getElementsByTagName('input');
    for (e = 0; e < elements.length; e++) {
      id = elements[e].getAttribute('id');
      lookupId = lookup[id].rules; var rules;
      // Loop through rules of the matched ID's
      for (rules of lookupId){
        // Check if there is a min length rule
        if(rules.name === 'min_length') {
          minLength = rules.value.toString();
          // Check if the rule is valid (is a number)
          if(isNaN(minLength) || minLength.length === 0){
            // Log an error somewhere here
          // Run the minLength function and report an error if it fails validation
          } else if(!checkMinLength(minLength, id)) {
            errors[errors.length] = id + " - You must enter more than " + minLength + " characters";
          }
        }
      }
      // If there are errors, report them
      if (errors.length > 0) {
        reportErrors(errors);
        //e.preventDefault();
      }
    }
  }
  validate();

  // Check the length of the field
  function checkMinLength(minLength, id){
    var val = document.getElementById(id).value;
    if(val < minLength){
      return false;
    }
    return true;
  }

  // Error reporting
  function reportErrors(errors){
    for (var i=0; i<errors.length; i++) {
        var msg = errors[i];
    }
    console.log(msg);
  }

  $('#email-submit').on('click',function(e){
      validate(e);
  });

}

Possibly not relevant but below is the json that is loaded...

[
  {
    "id": "search",
    "rules": [
      {
        "name": "min_length",
        "value": "5"
      },
      {
        "name": "email"
      }
    ]
  },
  {
    "id": "phone-number",
    "rules": [
      {
        "name": "min_length",
        "value": 8
      }
    ]
  },
  {
    "id": "surname",
    "rules": [
      {
        "name": "min_length",
        "value": 10
      }
    ]
  }
]

And the basic form to validate...

<form action="index.html" name="searchForm" id="search-form">
            <label for="search">Email</label>
  <input type="text" id="search" name="email" placeholder="Enter email">
  <input type="text" id="phone-number" name="name" placeholder="Enter name">
        <button type="submit" id="email-submit">Submit</button>
    </form>

The code does exactly what you tell it to do

// Error reporting
function reportErrors(errors){
  for (var i=0; i<errors.length; i++) {
    var msg = errors[i];  <-- setting the variable on each iteration
  }
  console.log(msg);  <-- reads the last value from the last iteration
}

You need to move the console inside of the for loop

// Error reporting
function reportErrors(errors){
  for (var i=0; i<errors.length; i++) {
    var msg = errors[i];  
    console.log(msg);  
  }
}

or do not even loop

// Error reporting
function reportErrors(errors){
  console.log(errors.join("\n"));        
}

And now a logic issue, you are declaring a function inside a for loop

function response(responseData) {
  // ignored code //

  var elements = document.getElementsByTagName('input');
  for (e = 0; e < elements.length; e++) {
    function validate(e) {  <-- THIS SHOULD NOT BE IN THE FOR LOOP

Again just like the error message thing, only the last one is going to be there...

Create more readable scheme without closure.

var submitButton = document.querySelector('#email-submit')

function validate (evt) {
  async function loadRules (url) {
    var rawData = await fetch(url)
    return await rawData.json()
  }

  function reportErrors (error) {
    evt.preventDefault()
    // Report logic here
  }

  function evaluate (rules) {
    // Your validate rules here
    // loaded rules in rules object
    // eg. rules[0]['rules'].name
  }

  loadRules('js/rules.json').then(evaluate)
}

submitButton.addEventLister('click', validate)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM