Скрипт в Internet Explorer (IE) зависает и становится безответственным с пустым кешем (отлично работает с кешем) - PullRequest
1 голос
/ 22 декабря 2010

Хорошо, я собрал скрипт, который делает это:

  1. Сделать ajax-запрос (через getHTTPObject (), библиотеки не используются)
  2. Создать iframe со скриптом,src - это "blank.html".
  3. используйте iframe.document.write () для выполнения сценариев (включая сценарии на основе document.write) в iframe.
  4. вызов документа родительского окна для клонированиясодержимое iframe.
  5. Добавление клона содержимого к родительскому телу.

Работает как чудо во всех браузерах, кроме IE, где каждая версия - включая бета-версию IE9 - висит на iframeWindow.document.close () с пустым кешем, оставляя окно / вкладку безответственным.Когда я принудительно завершаю работу, перезапускаю и загружаю страницу снова, она работает.

То, что я уже пробовал:

  • Googled.
  • вызвал обратный вызов ajax-запроса вручнуюсо строкой вместо request.responseText - здесь работает даже с пустым кешем.
  • Removed document.close () - в результате скрипты в iframe вообще не выполняются (опять же, только с пустым кешем, кэшированные страницы работают нормально).
  • Протестировано, чтобы сделать ajax-запрос синхронным - без разницы.

Есть идеи?

Живой пример здесь: http://labs.adeprimo.se/~adupanyt/ot/unlimited_scroll/

Вот кодФункции install (), finish () и append () управляют iframe.

/*!
 * Cross browser unlimited scroll snippet
 * Copyright (c) 2010 by Adeprimo.
 * Released under the MIT license. 
 */
/* Code assumptions:
 *
 *      <div id="unlimited-scroll-wrapper">
 *
 *          ... first content ...
 *
 *          <a id="load-more-unlimited-content" href="/nyheter">
 *            Ladda mer innehåll</a>
 *      </div>
 */
(function(window, document, undefined){
    /**
     * This snippet has two running modes, and it is the load_on_scroll
     * directive which rules it.
     * 
     * true     = a scroll event is initiated, which will wait 
     *            until the bottom is reached before it load any new content.
     * false    = this script will continue loading more content, one piece 
     *            at the time, until there is no more to get.
     *
     */
    load_on_scroll = false;

    var request;    // results of getHTTPObject()
    var wrapper;    // reference to the element which stores the new contents.
    var callsCount; // keep the count of ajax calls, helps css targeting.
    var attachEvent;// stores result of window.attachEvent for performance

    // locks, these are updated by the script.
    window.mkt_nothing_more_to_load = false;// true when end of queue reached. 
    window.mkt_prevent_new_loading = false; // true when ajax in progress.    

    wrapper = document.getElementById('unlimited-scroll-wrapper');
    callsCount = 1;
    attachEvent = window.attachEvent;

    /**
     * Customize this function for your need.
     * It is called each time a new document fragment
     * shall load. 
     * In here, you might add some nifty jQuery or Prototype if your app
     * has it.
     */
    function load_and_append() {
        var src; // href attribute of the #load-more-unlimited-content A element

        // get the source
        src = document.getElementById('load-more-unlimited-content').href;

        // mktwebb specific, will only return the content and 
        // not the header or footer. 
        src = src.replace(/\?.+?$/, '') + '?segment=content,footer';

        if (!request) { 
            request = getHTTPObject();  // getHTTPObject must be declared 
        }                               // separately!

        if (request) {
            request.onreadystatechange = function() {
                if (request.readyState == 4) {
                    if (request.status == 200 || request.status == 304) {
                        console.log('append() begin');
                        append(request.responseText, src);
                        console.log('append() done');
                    }
                }
            };

            request.open("GET", src, false);
            request.send(null);
        }
    }

    function finish(iframe_window) {
        var acc_elm = (function(doc){
            return doc.getElementsByTagName('div')[0].cloneNode(true);
        })(iframe_window.document);

        document.getElementById('unlimited-scroll-wrapper').appendChild(acc_elm);

        window.mkt_prevent_new_loading = false; // we are ready to more content.

        // we are done with the iframe, let's remove it.
        var iframe_container = document.getElementById('mkt_iframe-container');
        iframe_container.parentNode.removeChild(iframe_container);

        // basically, the script continues as long as it finds a new 
        // #load-more-unlimited-content element in the newly added content. 
        // if it can't find the #load-more-unlimited-content,
        // the script will stop and unattach itself.
        if (document.getElementById('load-more-unlimited-content')) {
            // if load_on_scroll, the new content is added under 
            // the control of the scroll event. There is no need to call
            // load_and_append here since the scroll event will manage
            // that fine.
            // however, when not load_on_scroll,
            // new content should be loaded asap.
            if (!load_on_scroll) {
                window.mkt_prevent_new_loading = true;
                // give the browser some time to reflow and rest, then continue.
                setTimeout(load_and_append, 2 * 1000);
            }
        } else {
            nothing_more_to_load = true; // tell the scroll event to stop.

            // remove scroll event since it is not needed anymore 
            if (attachEvent) {
                window.detachEvent('onscroll', 
                    look_for_trouble); // ie
            } else {
                window.removeEventListener('scroll', 
                    look_for_trouble, false); // w3c
            }
        }
    };
    window['mkt_importFromIframe'] = finish;

    // ------------------------------------------------------------------------
    // We are now ready to start.
    // ------------------------------------------------------------------------

    // see head section in this file for how to configure load_on_scroll
    function init() {
        if (load_on_scroll) {
            if (attachEvent) {
                window.attachEvent('onscroll', look_for_trouble);
            } else {
                window.addEventListener('scroll', look_for_trouble, false);
            }
        } else {
            load_and_append();
        }
    }

    // we are using window.onload since we want "everything" to run
    // in the first screeen before we continue.
    if (attachEvent) {
        window.attachEvent('onload', init);
    } else {
        window.addEventListener('load', init, false);
    }

    // ------------------------------------------------------------------------
    // the script has started. Below functions are supporting it.
    // ------------------------------------------------------------------------

    // loaded in load_and_append after a successful ajax call.
    function append(txt, src) {
        // remove previously #load-more-unlimited-content links since 
        // it i not needed anymore.
        (function(elm_to_remove){ 
            elm_to_remove.parentNode.removeChild(elm_to_remove); 
        })(document.getElementById('load-more-unlimited-content'));

        console.log('install() begin');
        install(txt, src);
        console.log('install() done');
    }

    /**
     * cleaning function to strip unecessary tags(oup) out.
     * also attach and execute scripts with the help of an 
     * handy little snippet.
     */
    function install(ajax_result, src) {
        var acc_elm;        // final wrapper DIV,
        var acc_class;      // and it's unique class.

        // remove the footer since we don't want that in our result.
        ajax_result = ajax_result.match(/^([\n\s\S]+?)<div id="mainBottom/im)
                    || ['', ajax_result];

        // rename #startPageContainer to avoid css conflicts.
        ajax_result = ajax_result[1].replace(/startpageContainer/m, 
                                'startPageContainer_' 
                                + Math.floor(Math.random() * 1000));        

        ajax_result = ajax_result.replace(/<scr/ig, '\x3Cscr').replace(/<\/scr/ig, '\x3C/scr');

        acc_class = 'aCC-' + src.match(/:\/\/.+?\/(.+?)\?/)[1].replace(/[\/.]/g, '_');
        acc_class += ' aCC'+ ++callsCount;

        acc_class = 'allColumnsContainer ' + acc_class;

        // mount ajax response in a temporary iframe 
        // and send the markup upwards when done. by doing 
        // this, all scripts in the ajax response is executed 
        // correctly.
        console.log('iframe begin');
        (function(iframe_container){
            var iframe_window;  // reference
            var splitted;       // for cross browser script execution

            iframe_window = getIFrameWindow(document.getElementById('unlimited-loader'));

            iframe_window.document.open();

            // open wrappers DIVs.
            iframe_window.document.write(
                '<div class="' + acc_class + 
                '"><div class="allColumnsContainer-inner">');

            // now comes a tricky part: all but IE will execute
            // script tags without any complains. To make the
            // last stubborn one work with us, we need to split 
            // strings to mimic the rusty ol':
            //       '<scr' + 'ipt>doAmazingStuff()</scr' + 'ipt>';
            splitted = ajax_result.split('<scr');

            // first chunk can be added right away.
            iframe_window.document.write(splitted[0]);

            // we are done now unless there was a script in 
            // the ajax response, which we know if we have more
            // than one chunk.
            if (splitted.length > 1) {
                for (var extracted, i = 1, max = splitted.length; i < max; i++) {
                    // this is necessary since we need to 
                    // split the end tags as well.
                    extracted = splitted[i].split('</scr');

                    // now we can put it together
                    iframe_window.document.write('<scr' 
                        + extracted[0] + '</scr' + extracted[1]);
                }
            }

            // close wrapper DIVs
            iframe_window.document.write('</div></div>');

            // finally, we ask the iframe to send the html 
            // up to the parent.
            iframe_window.document.write('<scr');
            iframe_window.document.write('ipt>setTimeout(function(){parent.mkt_importFromIframe(this);}, 999);</scr' + 'ipt>');
            iframe_window.document.write('<h1>bu!</h1>');

            console.log('iframe document.close begin');

            console.log('iframe document.close done');
        })((function(){
            var div = document.createElement('div');

            // the iframe should be visually hidden so
            // lets add some css for that.
            div.style.position = "absolute";
            div.style.left = "-9999px";
            div.style.top = "-99px";

            div.id = 'mkt_iframe-container'; // do not change this!

            div.innerHTML = '<iframe id="unlimited-loader" name="unlimited-loader" src="inner.html"></iframe>';

            (function(scr){
                 scr.parentNode.insertBefore(div, scr);
            })(document.getElementsByTagName('script')[0]);

            return div;
        })());    
        console.log('iframe done');
    }

    /**
     * callback function which is called when we are using 
     * scrollbased loading and fires a scroll event.
     * It makes sure we are not loading anything
     * until it is necessary.
     */
    function look_for_trouble(e) {
        // first, check to see if we should continue.
        if (window.mkt_nothing_more_to_load || window.mkt_prevent_new_loading) { 
            return; // one or more locks is still active, so we wait.
        }

        // second, we only want to load new content 
        // if we are at the bottom of the page.
        if (getDocHeight() - getScrollTop() <= window.outerHeight) {
            window.mkt_prevent_new_loading = true;
            load_and_append();
        }
    };

    //
    // borrowed functions.
    //

    // found at http://james.padolsey.com/javascript/get-document-height-cross-browser/
    function getDocHeight() {
        return Math.max(
            Math.max(document.body.scrollHeight, 
                document.documentElement.scrollHeight),
            Math.max(document.body.offsetHeight, 
                document.documentElement.offsetHeight),
            Math.max(document.body.clientHeight, 
                document.documentElement.clientHeight)
        );
    }

    // found at /758784/kross-brauzernyi-metod-dlya-opredeleniya-scrolltop-okna-brauzera
    function getScrollTop(){
        if(typeof pageYOffset!= 'undefined'){
            //most browsers
            return pageYOffset;
        }
        else{
            var B= document.body; //IE 'quirks'
            var D= document.documentElement; //IE with doctype
            D= (D.clientHeight)? D: B;
            return D.scrollTop;
        }
    }

    // http://av5.com/docs/changing-parent-window-s-url-from-iframe-content.html
    function getIFrameWindow(iframe) {
        return (iframe.contentWindow) ? iframe.contentWindow : (iframe.contentDocument.document) ? iframe.contentDocument.document : iframe.contentDocument;
    }

    function getHTTPObject() {
        var xhr = false;//set to false, so if it fails, do nothing
        if(window.XMLHttpRequest) {//detect to see if browser allows this method
            var xhr = new XMLHttpRequest();//set var the new request
        } else if(window.ActiveXObject) {//detect to see if browser allows this method
            try {
                var xhr = new ActiveXObject("Msxml2.XMLHTTP");//try this method first
            } catch(e) {//if it fails move onto the next
                try {
                    var xhr = new ActiveXObject("Microsoft.XMLHTTP");//try this method next
                } catch(e) {//if that also fails return false.
                    xhr = false;
                }
            }
        }
        return xhr;//return the value of xhr
    }
})(window, document);

1 Ответ

0 голосов
/ 22 декабря 2010

Может быть, я ошибаюсь, но насколько я знаю в соответствии со спецификацией DOM, в MSIE невозможно перемещать узлы между документами (и это то, что вы делаете, относительно вашего описания)

для тестирования:

<html>
<head>
<script type="text/javascript">
<!--
function fx(o)
{
  var doc=o.contentWindow.document;
  doc.body.appendChild(doc.createTextNode('This works with nodes \
                                           from the same document'));
  try{
      doc.body.appendChild(document.createTextNode(' and also with nodes \
                                                     from another document'));
     }
  catch(e)
    {
      doc.body.appendChild(doc.createTextNode(' but not  with nodes from \
                                                another document=>['+e.description+']'));
    }
}
//-->
</script>
</head>
<body>
<iframe onload="fx(this)" src="about:blank"></iframe>
</body>
</html>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...