jQuery validate large forms - script running slowl

2020-08-12 10:19发布

问题:

I'm using jQuery Validate plugin 1.8.0 with jQuery 1.5. Works great for small to medium sized forms. For larger forms the performance degrades significantly (even in IE8 and FF4), sometimes causing the "script is running too slowly" message. It appears that the plugin scans the entire DOM within the form looking for attributes and classes to validate, even if you specified custom rules. Anyone know how to turn this off completely? There is an ignore option as well, but it still would scan the DOM, skipping those with the ignore attr.

Here is what ASP.NET renders, except there are about 120 rows of data. Paging the results is not an option, unfortunately.

<table id="GridView1">
    <tbody>
        <tr>
            <th scope="col">Header 1</th>
            <th scope="col">Header 2</th>
            <th scope="col">Header 3</th>
            <th scope="col">Header 4</th>
            <th scope="col">Header 5</th>
            <th scope="col">Header 6</th>
            <th style="width: 60px; white-space: nowrap" scope="col">Header 7</th>
            <th style="width: 60px; white-space: nowrap" scope="col">Header 8</th>
        </tr>        
        <tr class="gridRow" jquery1507811088779756411="3">
            <td style="width: 50px" align="middle">
                <span id="GridView1_ctl03_Label1">XXX</span>
            </td>
            <td>
                <span id="GridView1_ctl03_Label2">YYY</span>
            </td>
            <td style="width: 50px" align="middle">
                <span id="GridView1_ctl03_Label3">ZZZ</span>
            </td>
            <td align="middle">
                <select style="width: 70px" id="GridView1_ctl03_Dropdown4" name="GridView1$ctl03$Dropdown4">
                    <option selected value="Y">Y</option>
                    <option value="N">N</option>
                </select>
            </td>
            <td style="width: 50px" align="middle">
                <input id="GridView1_ctl03_hidId1" value="100" type="hidden" name="GridView1$ctl03$hidId1" />
                <input id="GridView1_ctl03_hidId2" value="100" type="hidden" name="GridView1$ctl03$hidId2" />
                <input id="GridView1_ctl03_hidId3" value="100" type="hidden" name="GridView1$ctl03$hidId3" />
                <input id="GridView1_ctl03_hidId4" value="100" type="hidden" name="GridView1$ctl03$hidId4" />
                <select style="width: 40px" id="GridView1_ctl03_Dropdown5" name="GridView1$ctl03$Dropdown5">
                    <option selected value="A">A</option>
                    <option value="B">B</option>
                </select>
            </td>
            <td style="width: 50px" align="middle">
                <span id="GridView1_ctl03_Label6">101</span>
            </td>
            <td align="middle">
                <input style="width: 60px" id="GridView1_ctl03_Textbox8" class="date required"
                    title="Please enter a valid start date." type="text" name="GridView1$ctl03$Textbox8"
                    jquery1507811088779756411="122" />
            </td>
            <td align="middle">
                <input style="width: 60px" id="GridView1_ctl03_Textbox9" class="date"
                    title="Please enter a valid end date." type="text" name="GridView1$ctl03$Textbox9"
                    jquery1507811088779756411="123" />
            </td>
        </tr>
    </tbody>
</table>

回答1:

I've been struggling with this issue as well. By customizing some of the validation, I've been able to reduce my validation time in IE8 for 80 validating elements from 4100ms to 192ms. I'll post my findings here in the hope that others can benefit, and also in the hope that some expert in jquery-validate will find a problem with my code.

Here are some things that I found helpful:

  1. Make sure you don't have validation attributes on anything you don't actually need to validate. I had some mysteriously showing up on elements - I'm not sure why, but I hard-coded data-val=false on them in my .cshtml to prevent this.
  2. Define your own method to validate the form. The one built in to jQuery does a couple things very slowly, and you likely don't need all the flexibility it provides. Here is mine - using this made a HUGE difference (it's called subset because my form is split up into tabs and I call this on each tab div individually as the user advances).

    jQuery.validator.prototype.subset = function (container, validateHiddenElements) {
        var ok = true;
        var validator = this;
    
        // Performance hack - cache the value of errors(), and temporarily patch the    function to return the cache
        // (it is restored at the end of this function).
        var errors = this.errors();
        var errorsFunc = this.errors;
        this.errors = function () { return errors; };
    
        $(container.selector + " [data-val=true]").each(function () {
    
            !this.name && validator.settings.debug && window.console && console.error("%o has no name assigned", this);
    
            var tagName = this.tagName.toLowerCase();
            if (tagName == "input" || tagName == "select" || tagName == "textarea") {
                var $this = $(this);
    
                if (!$this.hasClass('doNotValidate') &&
                    (validateHiddenElements || $this.is(':visible'))) {
    
                    if (!validator.element($this)) ok = false;
    
                }
            }
        });
    
        this.errors = errorsFunc;
    
        return ok;
    };
    
  3. Define your own showErrors method on validator.settings. The built-in one creates error message spans for each validatable input even when there is no error to display. This gets quite slow if you have a lot of these, so you can add some logic to avoid this. Here is mine:

    function showErrorsOverride() {
        var anyElementsNeedUpdate = false;
        for (var i = 0; i < this.errorList.length; i++) {
            if (!$(this.errorList[i].element).hasClass(this.settings.errorClass)) {
            anyElementsNeedUpdate = true;
            }
        }
        for (var i = 0; i < this.successList.length; i++) {
            if ($(this.successList[i]).hasClass(this.settings.errorClass)) {
                anyElementsNeedUpdate = true;
            }
        }
    
    
        if (anyElementsNeedUpdate) 
        {
            // show the usual errors (defaultShowErrors is part of the jQuery validator)
            this.defaultShowErrors();
        }
    }
    


回答2:

A bit late to the party, but for anybody interested - similar to Joe's answer, I found that disabling the success setting from the defaultShowErrors() function helps out quite a bit. All it seems to do is hide the error label for valid fields, and if you don't have any error labels then it is unnecessary overhead. Sped up my form with 55 fields from ~1.8 seconds to ~260ms in IE8.

$(document).ready(function()
{
    $("form").each(function ()
    {
        $(this).data("validator").settings.success = false;
    })
});


回答3:

We need more code to help you. But traversing DOM objects it's a heavy operation. Maybe for that big forms you want to use another approach. If you are using a lot of combos and textfields you can attach an event handler (on lost focus) so you can save the value in a javascript object and later use this object to get the data.



回答4:

We had similar issues with large form but found the following solution. Now our large forms (>600 inputs) validate in ~10ms.

I've posted the answer here: https://stackoverflow.com/a/23132844/1821717