链接元素onload

时间:2010-06-20 08:06:53

标签: javascript css dom javascript-events hyperlink

有没有听一下<link>元素的onload事件?

F.ex:

var link = document.createElement('link');
link.rel = 'stylesheet';
link.href = 'styles.css';

link.onload = link.onreadystatechange = function(e) {
    console.log(e);
};

这适用于<script>个元素,但不适用于<link>。还有另外一种方法吗? 我只需要知道外部样式表中的样式何时应用于DOM。

更新

注入隐藏的<iframe>是一个想法,将<link>添加到头部并在iframe中监听window.onload事件?它应该在加载css时触发,但它可能无法保证它被加载到顶部窗口......

11 个答案:

答案 0 :(得分:16)

今天,所有现代浏览器都支持链接标记上的onload事件。所以我会防范黑客攻击,比如创建一个img元素并设置onerror:

if !('onload' in document.createElement('link')) {
  imgTag = document.createElement(img);
  imgTag.onerror = function() {};
  imgTag.src = ...;
} 

这应该为FF-8以及早期和旧的Safari&amp; amp;提供解决方法。 Chrome版本。

次要更新:

正如迈克尔所指出的那样,有一些浏览器例外,我们总是希望应用黑客攻击。在Coffeescript中:

isSafari5: ->
  !!navigator.userAgent.match(' Safari/') &&
      !navigator.userAgent.match(' Chrom') &&
      !!navigator.userAgent.match(' Version/5.')

# Webkit: 535.23 and above supports onload on link tags.
isWebkitNoOnloadSupport: ->
  [supportedMajor, supportedMinor] = [535, 23]
  if (match = navigator.userAgent.match(/\ AppleWebKit\/(\d+)\.(\d+)/))
    match.shift()
    [major, minor] = [+match[0], +match[1]]
    major < supportedMajor || major == supportedMajor && minor < supportedMinor

答案 1 :(得分:7)

这是一种破解,但是如果你可以编辑CSS,你可以添加一个特殊的样式(没有可见的效果),你可以使用这篇文章中的技术来监听:http://www.west-wind.com/weblog/posts/478985.aspx

您需要页面中具有CSS将影响的类或ID的元素。当您的代码检测到其样式已更改时,CSS已加载。

黑客,正如我所说:)

答案 2 :(得分:3)

我在Chrome上执行此操作的方式(未在其他浏览器上测试)是使用Image对象加载CSS并捕获其onerror事件。问题是浏览器不知道这个资源是否是图像,所以无论如何它都会尝试获取它。但是,由于它不是实际图像,因此会触发onerror个处理程序。

var css = new Image();
css.onerror = function() {
    // method body
}
// Set the url of the CSS. In link case, link.href
// This will make the browser try to fetch the resource.
css.src = url_of_the_css;

请注意,如果资源已被提取,则此提取请求将到达缓存。

答案 3 :(得分:2)

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

"onload" in link === true

因此,我的解决方案是从userAgent检测Android浏览器,然后在样式表中等待一些特殊的css规则(例如,重置为&#34; body&#34; margin)。
如果它不是Android浏览器,它支持&#34; onload&#34;事件 - 我们将使用它:

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

答案 4 :(得分:1)

既然你不喜欢我的黑客攻击:)我四处寻找其他方式并发现one by brothercake

基本上,建议使用AJAX来使浏览器对其进行缓存,然后将链接加载视为瞬时,因为CSS已缓存。这可能不会每次都有效(例如,某些浏览器可能会关闭缓存),但几乎总是如此。

答案 5 :(得分:1)

另一种方法是检查加载的样式表的数量。例如:

使用“css_filename”css文件的url或文件名,并在加载css时“回调”回调函数:

var style_sheets_count=document.styleSheets.length;
var css = document.createElement('link');
css.setAttribute('rel', 'stylesheet');
css.setAttribute('type', 'text/css');
css.setAttribute('href', css_filename);
document.getElementsByTagName('head').item(0).appendChild(css);
include_javascript_wait_for_css(style_sheets_count, callback, new Date().getTime());

function include_javascript_wait_for_css(style_sheets_count, callback, starttime)
/* Wait some time for a style sheet to load.  If the time expires or we succeed
 *  in loading it, call a callback function.
 * Enter: style_sheet_count: the original number of style sheets in the
 *                           document.  If this changes, we think we finished
 *                           loading the style sheet.
 *        callback: a function to call when we finish loading.
 *        starttime: epoch when we started.  Used for a timeout. 12/7/11-DWM */
{  
   var timeout = 10000; // 10 seconds
   if (document.styleSheets.length!=style_sheets_count || (new Date().getTime())-starttime>timeout)
      callback();
   else
      window.setTimeout(function(){include_javascript_wait_for_css(style_sheets_count, callback, starttime)}, 50);
}

答案 6 :(得分:0)

您需要一个您知道的样式的特定元素,或者如果您控制CSS文件,您可以为此目的插入一个虚拟元素。当css文件的内容应用于DOM时,此代码将完全使您的回调运行。

// dummy element in the html
<div id="cssloaded"></div>

// dummy element in the css
#cssloaded { height:1px; }

// event handler function
function cssOnload(id, callback) {
  setTimeout(function listener(){
    var el = document.getElementById(id),
        comp = el.currentStyle || getComputedStyle(el, null);
    if ( comp.height === "1px" )
      callback();
    else
      setTimeout(listener, 50);
  }, 50)
}

// attach an onload handler
cssOnload("cssloaded", function(){ 
  alert("ok"); 
});

如果您在文档底部使用此代码,则可以将 el comp 变量移出计时器之外,以便获取元素一次。但是如果你想在文档中的某个地方(比如头部)附加处理程序,你应该按原样保留代码。

注意:在FF 3 +,IE 5.5 +,Chrome

上测试

答案 7 :(得分:0)

这个技巧来自xLazyLoader jQuery plugin

var count = 0;

(function(){
  try {
    link.sheet.cssRules;
  } catch (e) {
    if(count++ < 100)
      cssTimeout = setTimeout(arguments.callee, 20);
    else
      console.log('load failed (FF)');
    return;
  };
  if(link.sheet.cssRules && link.sheet.cssRules.length == 0) // fail in chrome?
    console.log('load failed (Webkit)');
  else
    console.log('loaded');
})();

在FF(3.6.3)和Chrome(linux - 6.0.408.1 dev)

中进行本地测试和工作

演示here(请注意,这不适用于跨站点css加载,如在演示中,在FF下完成)

答案 8 :(得分:0)

xLazyLoader插件失败,因为对于属于其他域的样式表隐藏了cssRules属性(中断了相同的源策略)。所以你要做的就是比较ownerNode和owningElements。

以下是对待办事项的详尽解释: http://yearofmoo.com/2011/03/cross-browser-stylesheet-preloading/

答案 9 :(得分:0)

// this work in IE 10, 11 and Safari/Chrome/Firefox/Edge
// if you want to use Promise in an non-es6 browser, add an ES6 poly-fill (or rewrite to use a callback)

let fetchStyle = function(url) {
  return new Promise((resolve, reject) => {
    let link = document.createElement('link');
    link.type = 'text/css';
    link.rel = 'stylesheet';
    link.onload = resolve;
    link.href = url;

    let headScript = document.querySelector('script');
    headScript.parentNode.insertBefore(link, headScript);
  });
};

答案 10 :(得分:0)

这是跨浏览器的解决方案

// Your css loader
var d = document,
    css = d.head.appendChild(d.createElement('link'))

css.rel = 'stylesheet';
css.type = 'text/css';
css.href = "https://unpkg.com/tachyons@4.10.0/css/tachyons.css"

// Add this  
if (typeof s.onload != 'undefined') s.onload = myFun;
} else {
    var img = d.createElement("img");
    img.onerror = function() {
      myFun();
      d.body.removeChild(img);
    }
    d.body.appendChild(img);
    img.src = src;
}

function myFun() {
    /* ..... PUT YOUR CODE HERE ..... */
}

答案基于this link,其内容为:

  

幕后发生的事情是浏览器尝试加载    img 元素中的CSS,并且因为样式表不是   图像中, img 元素引发 onerror 事件并执行   功能。值得庆幸的是,浏览器在加载整个CSS文件之前   确定其不是图像并触发 onerror 事件。

在现代浏览器中,您可以执行css.onload并将该代码添加为后备版本,以覆盖2011年以前的旧浏览器,当时只有Opera和Internet Explorer分别支持onload事件和onreadystatechange 。< / p>

注意:我也回答了here,这是我的副本,应为我的诚实而受到惩罚:P