Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jQuery validate large forms - script running slowly

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>
like image 676
Jeff Avatar asked Apr 04 '11 17:04

Jeff


1 Answers

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();
        }
    }
    
like image 126
Joe Strommen Avatar answered Sep 19 '22 12:09

Joe Strommen