saber 酱的抱枕

Fly me to the moon

02/5
2016
学习

实现从父窗口中监测iframe的Ready事件的办法

试想如果我们创建了一个iframe,然后需要访问其中的元素。这时候我们可以对iframe使用onload事件,在其完全载入后执行我们需要的操作。

但是onload事件需要等到图片等资源完全加载完成才执行,这时我想到了jquery的ready()方法,在DOM加载完成后即可执行,更加省时。

但是jquery的ready()方法对框架是无效的。然后我去寻找JavaScript原生的事件,以此来代替jquery的ready()方法。今天终于找到了可行的办法,也是用JavaScript原生的事件做的,实现了和jquery的ready()方法类似的效果。这个代码封装成了函数,我们使用的时候在父窗口调用即可。

代码比较长,我就贴下边了。

这个方法不适用于有跨域问题的情况。下面测试中iframe的url是pixiv上的,可以去pixiv的网页上在控制台执行。

//创建框架
var newFrame=document.createElement("iframe");
newFrame.name="newFrame";
newFrame.id="newFrame";
newFrame.width=300;
newFrame.height=300;
document.body.appendChild(newFrame); 
newFrame.src="http://www.pixiv.net/member_illust.php?mode=medium&illust_id=54474518";

//封装ready的函数,框架需要和父窗口同源
function iFrameReady(iFrame, fn) {
    var timer;
    var fired = false;

    function ready() {
        if (!fired) {
            fired = true;
            clearTimeout(timer);
            fn.call(this);
        }
    }

    function readyState() {
        if (this.readyState === "complete") {
            ready.call(this);
        }
    }

    // cross platform event handler for compatibility with older IE versions
    function addEvent(elem, event, fn) {
        if (elem.addEventListener) {
            return elem.addEventListener(event, fn);
        } else {
            return elem.attachEvent("on" + event, function () {
                return fn.call(elem, window.event);
            });
        }
    }

    // use iFrame load as a backup - though the other events should occur first
    addEvent(iFrame, "load", function () {
        ready.call(iFrame.contentDocument || iFrame.contentWindow.document);
    });

    function checkLoaded() {
        var doc = iFrame.contentDocument || iFrame.contentWindow.document;
        // We can tell if there is a dummy document installed because the dummy document
        // will have an URL that starts with "about:".  The real document will not have that URL
        if (doc.URL.indexOf("about:") !== 0) {
            if (doc.readyState === "complete") {
                ready.call(doc);
            } else {
                // set event listener for DOMContentLoaded on the new document
                addEvent(doc, "DOMContentLoaded", ready);
                addEvent(doc, "readystatechange", readyState);
            }
        } else {
            // still same old original document, so keep looking for content or new document
            timer = setTimeout(checkLoaded, 1);
        }
    }
    checkLoaded();
}

//DOM加载完成即可执行
iFrameReady(document.getElementById("newFrame"), function() {
    var fonud = this.getElementsByClassName("after")[0].getElementsByTagName("a")[0];
//注意上面的this,this==document.getElementById("newFrame").contentDocument
//也可以换种写法 this==window.newFrame.contentDocument
    console.log(fonud);
});

/*参考来源
http://stackoverflow.com/questions/24603580/how-can-i-access-the-dom-elements-within-an-iframe
*/

调用的时候,两个参数第一个是iframe元素,第二个是函数。调用已有的函数也是可以的。

但是这个方法有个缺点,就是iframe加载完一个页面之后,始终是完成状态,即便你改变了src去加载另一个页面时也是如此。所以当你打算用同一个iframe加载多个页面,并且避免此问题时,可以移除此iframe节点,再重新创建一次。如下:

document.getElementById("iframe1").parentNode.removeChild(document.getElementById("iframe1"));//移除iframe
criframe1(nextUrl);// 重新创建iframe1

实现从父窗口中监测iframe的Ready事件的办法