Search code examples
javascriptjqueryjquery-uiprototypejsconflict

JS widget (using Jquery) conflicting with Prototype on host page


I am providing a Javascript widget to other sites, and my use of Jquery in the widget appears to be conflicting with the host site's use of Prototype.

You can see the conflict in action here: http://www.phillyrealestateadvocate.idxco.com/idx/8572/results.php?lp=100000&hp=500000&sqFt=0&bd=2&ba=0&searchSubmit=&city%5B%5D=131

On the right side of the page, click on the green "Ask a question" button - it will spawn a popup and as soon as you open the popup, "invalid array length" errors start rolling in to the JS console (and don't stop.) The popup is then unable to close but is still draggable. The code/content within the popup is in an iframe, so it still works normally, but the popup just won't close.

The error Firebug gives me is: invalid array length in Prototype.js, but when I expand that for details, it has references to jquery.min.js, so this led me to believe the two are conflicting.

My widget code is entirely in an anonymous function, and loads Jquery using the model described here by Alex Marandon: http://alexmarandon.com/articles/web_widget_jquery/

I am using a noConflict call, but perhaps it's not working the way I placed it?

Here is the beginning of my widget script which contains the references to jquery.min.js and jqueryui:

    (function() {

        // Localize jQuery variable
        var jQuery;

        /******** Load jQuery if not present *********/
        if (window.jQuery === undefined || window.jQuery.fn.jquery !== '1.6.2') 
        {
            var script_tag = document.createElement('script');
            script_tag.setAttribute("type","text/javascript");
            script_tag.setAttribute("src",
                "http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js");
            script_tag.onload = scriptLoadHandler;
            script_tag.onreadystatechange = function () { // Same thing but for IE
                if (this.readyState == 'complete' || this.readyState == 'loaded') {
                    scriptLoadHandler();
                }
            };
            // Try to find the head, otherwise default to the documentElement
            (document.getElementsByTagName("head")[0] || document.documentElement).appendChild(script_tag);
        } 
        else 
        {
            $.getScript( "https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.14/jquery-ui.min.js" , 
                function()
                {
                    // The jQuery version on the window is the one we want to use
                    jQuery = window.jQuery;
                    main();
                }
            );
        }

        /******** Called once jQuery has loaded ******/
        function scriptLoadHandler() 
        {
            $.getScript( "https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.14/jquery-ui.min.js" , 
                function()
                {
                    // Restore $ and window.jQuery to their previous values and store the
                    // new jQuery in our local jQuery variable
                    jQuery = window.jQuery.noConflict(true);
                    main(); 
                }
            );
        }

        /******** Our main function ********/
        function main() 
        { 

           // Do a bunch of stuff here

    }

})(); // We call our anonymous function immediately

Any help would be much appreciated!

Edit: I now have Jquery UI directly within the widget, so I eliminated the getScript calls altogether. Unfortunately, this did not fix the conflict. Here is the new code:

(function() {

    // Localize jQuery variable
    var jQuery, 

    /******** Load jQuery if not present *********/
    if (window.jQuery === undefined || window.jQuery.fn.jquery !== '1.6.2') 
    {
        var script_tag = document.createElement('script');
        script_tag.setAttribute("type","text/javascript");
        script_tag.setAttribute("src", "http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js");
        script_tag.onload = scriptLoadHandler;
        script_tag.onreadystatechange = function () { // Same thing but for IE
            if (this.readyState == 'complete' || this.readyState == 'loaded') {
                scriptLoadHandler();
            }
        };
        // Try to find the head, otherwise default to the documentElement
        (document.getElementsByTagName("head")[0] || document.documentElement).appendChild(script_tag);
    } 
    else 
    {
        jQuery = window.jQuery;
        main();
    }

    /******** Called once jQuery has loaded ******/
    function scriptLoadHandler() 
    {
        jQuery = window.jQuery.noConflict(true);
        main(); 
    }

    /******** Our main function ********/
    function main() 
    { 

Solution

  • The problem is that Prototype overrides the default Array.shift method in a way that is not fully compatible. jQuery expects the default behaviour.

    Actually it's not causing problem only to jQuery, when your page loads, you get a similar error caused by a call to shift in the curfon-yui.js script loaded on that page.

    Let's try it in Firebug without Prototype loaded:

      >>> [].shift()
      undefined
    

    Now on your page:

    >>> [].shift()
    RangeError: invalid array length
    this[i]=this[i+1];this.length--;return...ct).join(': ');}).join(', ')+'}>';}} 
    

    Apparently you're not the only one to have this problem: http://tommysetiawan.com/post/7887390641/jquery-and-prototype-conflict-array-shift

    jQuery's noConflict won't help with this unfortunately. The issue seems to be solved in more recent versions of Prototype, so if you have any control on the host page it might help to update its version of Prototype. The fact that its already causing errors with another script might be a good argument to convince the owner of that page to fix it. Otherwise you might need to modify your widget so that it doesn't call Array.shift or you could even try to monkey-patch Array.shift in a way that fixes it.