简体   繁体   中英

Why is jQuery's .each drastically slower in Safari than Firefox/Chrome?

This question is not looking for a solution to a specific problem but trying to understand why Safari is inefficient in this instance. When I talk about drastically slower, the code runs in Firefox and Chrome in under 1 second while Safari takes in the range of 30-90 seconds. It's likely already a documented issue but I don't know why.


The situation is that I have an HTML table that is fairly large. It's 1,000-1,500 rows by 40 columns wide. The structure looks something like:

<table id="myTablePlayers" class="tablesorter table table-striped table-bordered table-hover" style="overflow: visible">
    <thead>
        <tr>
          <th>...</th>
          <th>...</th>
          <th>...</th>
          <th>...</th>
          ...
          <th>...</th>
        </tr>
    </thead>
    <tbody>
        <tr class="playerData">
            <td>...</td>
            <td>...</td>
            <td>...</td>
            <td>...</td>
            ...
            <td>...</td>
        </tr>
        ...
    </tbody>
</table>

A number of form fields allow users to select and enter information that help filter out rows. The jQuery looks like:

function autoRank() {
    // auto number
    rank = 0;
    $("#myTablePlayers .playerData").each(function() {
        if ($(this).css("display") != "none") {
            rank++;
            $(this).find('td').eq(colRank).text(rank);
        }
    });
}

function filterTable() {
    // Need some error checking on input not number
    minGP = $("#mingp").val()
    teams = $("#teamFilter").val()
    position = $("#position").val()
    age = $("#age").val()

    $("#myTablePlayers .playerData").show();

    $("#myTablePlayers .playerData").each(function() {
        toHide = false;

        if (teams != "") {
            if ( !$(this).find('td').eq(colTeam).text().toUpperCase().includes(teams.toUpperCase())) {
                toHide = true;
            }
        }

        if ( Number($(this).find('td').eq(colGP).text()) < minGP ) {
            toHide = true;
        }

        if (position != "") {
            if (position == "D") {
                if ($(this).find('td').eq(colPos).text().indexOf("D") == -1) {
                    toHide = true;
                }
            } else if (position == "F") {
                if ($(this).find('td').eq(colPos).text().indexOf("D") != -1) {
                    toHide = true;
                }
            } else if ( $(this).find('td').eq(colPos).text() != position) {
                toHide = true;
            }
        }

        if (age != "") {
            column = Number($(this).find('td').eq(colAge).text())
            age = Number(age)
            if (  column < age || column >= age+1  ) {
                toHide = true;
            }
        }

        if (toHide == true) {
            $(this).hide();
        }

    });

    autoRank();
}

$("#teamFilter").on('change', filterTable);

$("#mingp").on('change', filterTable);

$("#position").on('change', filterTable);

$("#age").on('change', filterTable);

When I start pruning down the code, the offending code that takes a long time to run, regardless of what's inside the loop, seems to be $("#myTablePlayers .playerData").each(function() {...

I solved the issue by re-writing the code in vanilla JS but that doesn't answer why this code is so inefficient in just one browser.

Checking the DOM status by making inquiries via .css() can be extremely expensive. Instead of hiding/revealing elements with .hide() and .show() , add/remove a class. In your CSS:

.hidden { display: none; }

Then your .each() loop can just check for that class:

$("#myTablePlayers .playerData").each(function() {
    if (!$(this).hasClass("hidden")) {
        rank++;
        $(this).find('td').eq(colRank).text(rank);
    }
});

To hide something, you'd just add that class, and to show it you'd remove it:

    if (toHide) {
        $(this).addClass("hidden");
    }

And to show:

$("#myTablePlayers .playerData").removeClass("hidden");

Now, all those .find() and .text() calls are going to be expensive too. It would probably be worthwhile to initialize the table by going over it once and creating data properties on each <tr> to effectively cache the interesting values from each row. Lookups via jQuery's .data() will be significantly cheaper than looking by selector in the DOM (though modern DOM implementations are pretty fast).

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