javascript:在LINK上捕获加载事件

时间:2010-04-14 08:10:30

标签: javascript javascript-events

我正在尝试将事件处理程序附加到链接标记的load事件,以在样式表加载后执行某些代码。

new_element = document.createElement('link');
new_element.type = 'text/css';
new_element.rel = 'stylesheet';
new_element.href = 'http://domain.tld/file.css';
new_element.addEventListener('load', function() { alert('foo'); }, false);
document.getElementsByTagName('head')[0].appendChild(new_element)

我也尝试过onreadystatechange

new_element.onreadystatechange = function() { alert('foo'); }

不幸的是,这两种方法都没有导致触发警报。 此外,在使用addEventListener注册'load'事件的处理程序后,new_element.onload为null。这是正常的吗?

感谢, 安德鲁

ps:我可能不会使用任何框架来解决这个问题

10 个答案:

答案 0 :(得分:52)

在我看来,对于使用IMG标记及其onerror事件的此问题,这是一个更好的解决方案。这个方法可以完成工作而不需要循环,进行扭曲的样式遵守,或者在iframe中加载文件等。这个解决方案在文件加载时正确激活,如果文件已经被缓存,则立即激活(这比大多数DOM的讽刺性更好)加载事件处理缓存资产)。这是我博客上的一篇文章,解释了方法 - Back Alley Coder post - 我厌倦了没有合法的解决方案,享受!

var loadCSS = function(url, callback){
    var link = document.createElement('link');
        link.type = 'text/css';
        link.rel = 'stylesheet';
        link.href = url;

    document.getElementsByTagName('head')[0].appendChild(link);

    var img = document.createElement('img');
        img.onerror = function(){
            if(callback) callback(link);
        }
        img.src = url;
}

答案 1 :(得分:10)

对于CSS样式表(一般不是LINK元素)我使用手动间隔,通过戳它的规则长度。它适用于crossbrowser(AFAIT)。

try {
  if ( cssStylesheet.sheet && cssStylesheet.sheet.cssRules.length > 0 )
    cssLoaded = 1;
  else if ( cssStylesheet.styleSheet && cssStylesheet.styleSheet.cssText.length > 0 )
    cssLoaded = 1;
  else if ( cssStylesheet.innerHTML && cssStylesheet.innerHTML.length > 0 )
    cssLoaded = 1;
}
catch(ex){}

在上面的代码中,cssStylesheet是DOMElement。

答案 2 :(得分:4)

即使您添加内联:

<link rel="stylesheet" type="text/css" href="foo.css" onload="alert('xxx')"/>

它不会在FireFox中触发onload link个{{1}}元素事件。 (它将在IE中工作)

答案 3 :(得分:3)

所有信用都归于托比亚斯上面所说的,但这里的内容略有扩展:

function _cssIsLoaded(cssStylesheet) {
    var cssLoaded = 0;
    try {
        if ( cssStylesheet.sheet && cssStylesheet.sheet.cssRules.length > 0 )
            cssLoaded = 1;
        else if ( cssStylesheet.styleSheet && cssStylesheet.styleSheet.cssText.length > 0 )
            cssLoaded = 1;
        else if ( cssStylesheet.innerHTML && cssStylesheet.innerHTML.length > 0 )
            cssLoaded = 1;
        }
        catch(ex){ }

        if(cssLoaded) {
            // your css is loaded! Do work!
            // I'd recommend having listeners subscribe to cssLoaded event, 
            // and then here you can emit the event (ie. EventManager.emit('cssLoaded');
        } else {
            // I'm using underscore library, but setTimeout would work too
            // You basically just need to call the function again in say, 50 ms
            _.delay(_.bind(this._cssIsLoaded, this), 50, cssStylesheet);
        }
}

你用类似的东西(使用jquery)来调用它:

var link = $("<link>");
link.attr({
    type: 'text/css',
    rel: 'stylesheet',
    href: sheet
});

$("head").append(link);
// link.get(0), because you want the actual element, not jQuery-wrapped element
self._cssIsLoaded(link.get(0));

答案 4 :(得分:3)

link.innerHTML,link.styleSheet和cssRules都是很好的方法,但它们不适用于属于原始域之外的域的样式表(因此跨站点样式表加载失败)。当使用子域与域(例如www)或使用静态CNS时,这可能是非常意外的。而且它很烦人,因为元素没有同源限制。

这是一个解决方案,它为支持它的浏览器(IE和Opera)使用onload方法,但是对不支持的浏览器使用定时间隔,并比较ownerNode和owningElement节点,以检查样式表何时制作它进入DOM的方式。

http://www.yearofmoo.com/2011/03/cross-browser-stylesheet-preloading.html

答案 5 :(得分:2)

很好的射击马特。我已经用你的评论创建了这个助手。

var CSSload = function(link, callback) {
    var cssLoaded = false;
    try{
        if ( link.sheet && link.sheet.cssRules.length > 0 ){
            cssLoaded = true;
        }else if ( link.styleSheet && link.styleSheet.cssText.length > 0 ){
            cssLoaded = true;
        }else if ( link.innerHTML && link.innerHTML.length > 0 ){
            cssLoaded = true;
        }
    }
    catch(ex){ }
    if ( cssLoaded ){
        callback();
    }else{
        setTimeout(function(){
            CSSload(link);
        }, 100);
    }
};

用法:

var $link = $('<link rel="stylesheet" media="screen" href="file.css"/>');
$('head').append($link);
CSSload($link.get(0), function(){
  // do something onLoad
});

答案 6 :(得分:1)

此功能适用于所有浏览器以及跨域和相同域的情况,也可以处理javascripts和样式表的注入。

function loadScript(src, type, callback_fn) {
    var loaded = false, scrpt, img;
    if(type === 'script') {
        scrpt = document.createElement('script');
        scrpt.setAttribute('type', 'text/javascript')
        scrpt.setAttribute('src', src);

    } else if(type === 'css') {
        scrpt = document.createElement('link')
        scrpt.setAttribute('rel', 'stylesheet')
        scrpt.setAttribute('type', 'text/css')
        scrpt.setAttribute('href', src);
    }
    document.getElementsByTagName('head')[0].appendChild(scrpt);

    scrpt.onreadystatechange = function(){
        if (this.readyState === 'complete' || this.readyState === 'loaded') {
            if(loaded === false) {
                callback_fn();
            }
            loaded = true;
        }
    };

    scrpt.onload = function() {
        if(loaded === false) {
            callback_fn();
        }
        loaded = true;
    };

    img = document.createElement('img');
    img.onerror = function(){
        if(loaded === false) {
            callback_fn();
        }
        loaded = true;
    }
    img.src = src;
};

答案 7 :(得分:0)

E.g。 Android浏览器不支持元素的“onload”/“onreadystatechange”事件:http://pieisgood.org/test/script-link-events/
但它返回:

"onload" in link === true

所以,我的解决方案是从userAgent检测Android浏览器,然后在样式表中等待一些特殊的css规则(例如,重置“body”边距)。
如果它不是Android浏览器并且它支持“onload”事件 - 我们将使用它:

var userAgent = navigator.userAgent,
    iChromeBrowser = /CriOS|Chrome/.test(userAgent),
    isAndroidBrowser = /Mozilla\/5.0/.test(userAgent) && /Android/.test(userAgent) && /AppleWebKit/.test(userAgent) && !iChromeBrowser; 

addCssLink('PATH/NAME.css', function(){
    console.log('css is loaded');
});

function addCssLink(href, onload) {
    var css = document.createElement("link");
    css.setAttribute("rel", "stylesheet");
    css.setAttribute("type", "text/css");
    css.setAttribute("href", href);
    document.head.appendChild(css);
    if (onload) {
        if (isAndroidBrowser || !("onload" in css)) {
            waitForCss({
                success: onload
            });
        } else {
            css.onload = onload;
        }
    }
}

// We will check for css reset for "body" element- if success-> than css is loaded
function waitForCss(params) {
    var maxWaitTime = 1000,
        stepTime = 50,
        alreadyWaitedTime = 0;

    function nextStep() {
        var startTime = +new Date(),
            endTime;

        setTimeout(function () {
            endTime = +new Date();
            alreadyWaitedTime += (endTime - startTime);
            if (alreadyWaitedTime >= maxWaitTime) {
                params.fail && params.fail();
            } else {
                // check for style- if no- revoke timer
                if (window.getComputedStyle(document.body).marginTop === '0px') {
                    params.success();
                } else {
                    nextStep();
                }
            }
        }, stepTime);
    }

    nextStep();
}

演示:http://codepen.io/malyw/pen/AuCtH

答案 8 :(得分:0)

这个方法更适合我。

// Create a <link> and set it in an HTML node
const 
$link = document.createElement("link");
$link.setAttribute('rel', 'stylesheet');
$link.setAttribute('href', '/build/style/test.css');
document.head.appendChild($link);

// Event about load
onloadCSS($link).then(function(url) {
    console.log('CSS loaded: ' + url);
}).catch(function(e) {
    console.log('Failed to load CSS');
});

function onloadCSS($link){
    return new Promise(function(resolve, reject) {
        const 
        options = {
            headers: ({'Accept': 'text/css,*/*;q=0.1'})
        }, 
        url = $link.getAttribute('href');

        fetch(url, options).then(function(response) {
            if(response.status === 200){
                return resolve(url);
            }

            return reject({status: response.status});
        }).catch(function(e) {
            return reject(e);
        });      
    });
}

答案 9 :(得分:-4)

当页面本身加载时,href没有你需要应用你的东西的加载事件。

window.onload = function(){
//code here

}

使用此页面上的功能:http://www.dustindiaz.com/top-ten-javascript/

添加事件的body元素(在行中)