We have a web site that uses extensively jQuery and it works fine in Firefox and IE. However in Chrome, we are getting frequently (and semi-randomly) Uncaught TypeError: Cannot call method 'apply' of undefined (also other jQuery methods appear in place of apply).
We managed to track down the problem to jQuery method pushStack().
Original source code (jQuery 1.7.1):
// Take an array of elements and push it onto the stack
// (returning the new matched element set)
pushStack: function( elems, name, selector ) {
// Build a new jQuery matched element set
var ret = this.constructor();
// (etc.)
}
Instrumented code:
pushStack: function( elems, name, selector ) {
if (!(this instanceof jQuery.fn.init)) throw this;
// Build a new jQuery matched element set
var ret = this.constructor();
if (!(ret instanceof jQuery.fn.init)) {
console.log("pushStack>this: " + this.constructor);
console.log("pushStack>ret: " + ret.constructor);
throw ret;
}
// (etc.)
}
In most cases pushStack() runs correctly. However sometimes Chrome constructs an object of type Object instead of jQuery.fn.init. Console output:
pushStack>this: function ( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
return new jQuery.fn.init( selector, context, rootjQuery );
}
pushStack>ret: function Object() { [native code] }
Uncaught #<Object>
Did anybody encounter similar problem? Is it a (known) bug of Chrome?
Update
I managed to simplify our page, so that it could be loaded on its own. I filled bug in Chromium project project, the page for reproducing the issue is attached there.
The responses in Chromium bug tracker seem to confirm that this is a bug of Chrome browser.
The workaround solution is to "fix" pushStack() function in jQuery:
// Take an array of elements and push it onto the stack
// (returning the new matched element set)
pushStack: function( elems, name, selector ) {
// Build a new jQuery matched element set
var ret = this.constructor();
// Workaround for Chrome bug
if ((this instanceof jQuery.fn.init) && !(ret instanceof jQuery.fn.init)) {
// console.log("applying pushStack fix");
ret = new jQuery.fn.init();
}
// etc.
}
Here is an "unobtrusive" solution (for example, if you're pulling jQuery from a CDN). Save this in a .js file and include it after pulling in jQuery.
(function ($) {
var pushStackOrig, pushStackChrome;
pushStackOrig = $.fn.pushStack;
pushStackChrome = function ( elems, name, selector ) {
// Build a new jQuery matched element set
// Invoke the correct constructor directly when the bug manifests in Chrome.
//var ret = this.constructor();
var ret = new jQuery.fn.init();
if ( jQuery.isArray( elems ) ) {
push.apply( ret, elems );
} else {
jQuery.merge( ret, elems );
}
// Add the old object onto the stack (as a reference)
ret.prevObject = this;
ret.context = this.context;
if ( name === "find" ) {
ret.selector = this.selector + ( this.selector ? " " : "" ) + selector;
} else if ( name ) {
ret.selector = this.selector + "." + name + "(" + selector + ")";
}
// Return the newly-formed element set
return ret;
};
$.fn.pushStack = function (elems, name, selector) {
var ret;
try {
ret = pushStackOrig.call(this, elems, name, selector);
return ret;
}
catch (e) {
if (e instanceof TypeError) {
if (!(ret instanceof jQuery.fn.init)) {
ret = pushStackChrome.call(this, elems, name, selector);
return ret;
}
}
throw e;
}
};
}).call(this, jQuery);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With