Skip to content
Ben Scobie

2 min read

jQuery’s hide() and show() slow in Chrome

Whilst working on a project where I’m filtering elements on the client using JavaScript I came across a very interesting issue with Chrome.

I filter elements by running jQuery’s hide() or show() on them depending on what the user entered and there’s around 750 elements that need this filtering. I noticed that this was quite slow in Chrome as it took around 2-3 seconds for the page to become responsive whilst filtering. I tested the page in Internet Explorer 9 to confirm that the code I had written was to blame, but the filtering was instant. What the hell Chrome?

I then asked a friend who uses Firefox to test it out and it also filtered instantly. I did a bit of research on the subject and came across a few people with the same issue as well as a bug report for Chromium.

One of the answers on Stack Overflow had a work around that seemed to work fine for Chrome. Instead of using the hide() and show() methods I used the following (with overflow: hidden;) :

$element.css("margin-top", "0"); // For showing
$element.css("margin-top", "-1000000px"); // For hiding

Filtering was now instant again, but I remembered whilst searching for an answer I heard that negative top margins don’t work too well on older browsers. Well I fired up IETester to give it a test in some of the older versions of Internet Explorer and they were right. The elements still showing should collapse downwards but in Internet Explorer 8 and below they don’t collapse properly, but instead gaps are left where the hidden elements once were.

To fix this I ended up detecting if the user was using Chrome using my own version of the jQuery browser function which you can find below:

(function ($) {
	var userAgent = navigator.userAgent.toLowerCase();
	$.browser = {
		version: (userAgent.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/) || [0, "0"])[1],
		chrome: /chrome/.test(userAgent),
		safari: /webkit/.test(userAgent),
		opera: /opera/.test(userAgent),
		msie: /msie/.test(userAgent) && !/opera/.test(userAgent),
		mozilla: /mozilla/.test(userAgent) && !/(compatible|webkit)/.test(userAgent),
	};
})(jQuery);

And then I used the following to do the rest:

var $cssSelector = "display";
var $hideCSS = "none";
var $showCSS = "block";

if ($.browser.chrome) {
	$cssSelector = "margin-top";
	$showCSS = "0";
	$hideCSS = "-10000px";
}

$("#element").css($cssSelector, $showCSS); // For showing
$("#element").css($cssSelector, $hideCSS); // For hiding

I haven’t tested to see whether this works in earlier versions of Chrome but I can’t see it being much of an issue as Chrome automatically updates with each stable release.