Javascript 链接元素加载
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3078584/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
link element onload
提问by David Hellsing
Is there anyway to listen to the onload event for a <link>element?
无论如何要侦听<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);
};
This works for <script>elements, but not <link>. Is there another way?
I just need to know when the styles in the external stylesheet has applied to the DOM.
这适用于<script>元素,但不适用于<link>. 还有其他方法吗?我只需要知道外部样式表中的样式何时应用于 DOM。
Update:
更新:
Would it be an idea to inject a hidden <iframe>, add the <link>to the head and listen for the window.onloadevent in the iframe? It should trigger when the css is loaded, but it might not guarantee that it's loaded in the top window...
注入一个 hidden <iframe>,将 the 添加<link>到头部并window.onload在 iframe 中侦听事件是否是一个想法?它应该在加载 css 时触发,但它可能无法保证它已加载到顶部窗口中...
采纳答案by Peter Jaric
This is kind of a hack, but if you can edit the CSS, you could add a special style (with no visible effect) that you can listen for using the technique in this post: http://www.west-wind.com/weblog/posts/478985.aspx
这是一种技巧,但如果您可以编辑 CSS,您可以添加一个特殊的样式(没有可见的效果),您可以使用这篇文章中的技术来聆听:http: //www.west-wind.com /weblog/posts/478985.aspx
You would need an element in the page that has a class or an id that the CSS will affect. When your code detects that its style has changed, the CSS has been loaded.
您需要页面中的元素具有 CSS 会影响的类或 ID。当您的代码检测到其样式已更改时,CSS 已加载。
A hack, as I said :)
一个黑客,正如我所说:)
回答by mlangenberg
Today, all modern browsers support the onload event on link tags. So I would guard hacks, such as creating an img element and setting the onerror:
今天,所有现代浏览器都支持链接标签上的 onload 事件。所以我会保护黑客,比如创建一个 img 元素并设置 onerror:
if !('onload' in document.createElement('link')) {
imgTag = document.createElement(img);
imgTag.onerror = function() {};
imgTag.src = ...;
}
This should provide a workaround for FF-8 and earlier and old Safari & Chrome versions.
这应该为 FF-8 及更早和旧的 Safari & Chrome 版本提供解决方法。
minor update:
小更新:
As Michael pointed out, there are some browser exceptions for which we always want to apply the hack. In Coffeescript:
正如迈克尔指出的那样,有一些浏览器异常我们总是想应用黑客。在 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
回答by Serg Hospodarets
E.g. Android browser doesn't support "onload" / "onreadystatechange" events for element: http://pieisgood.org/test/script-link-events/
But it returns:
例如,Android 浏览器不支持元素的“onload”/“onreadystatechange”事件:http: //pieisgood.org/test/script-link-events/
但它返回:
"onload" in link === true
So, my solution is to detect Android browser from userAgent and then wait for some special css rule in your stylesheet (e.g., reset for "body" margins).
If it's not Android browser and it supports "onload" event- we will use it:
因此,我的解决方案是从 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();
}
回答by bellpeace
The way I did it on Chrome (not tested on other browsers) is to load the CSS using an Imageobject and catching its onerrorevent. The thing is that browser does not know is this resource an image or not, so it will try fetching it anyway. However, since it is not an actual image it will trigger onerrorhandlers.
我在 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;
Note that if the resource has already been fetched, this fetch request will hit the cache.
请注意,如果资源已经被获取,这个获取请求将命中缓存。
回答by Peter Jaric
Since you didn't like my hack :) I looked around for some other way and found one by brothercake.
因为你不喜欢我的 hack :) 我四处寻找其他方式,并通过兄弟蛋糕找到了一个。
Basically, what is suggested is to get the CSS using AJAX to make the browser cache it and then treat the link load as instantaneous, since the CSS is cached. This will probably not work every single time (since some browsers may have cache turned off, for example), but almost always.
基本上,建议是使用 AJAX 获取 CSS 以使浏览器缓存它,然后将链接加载视为即时,因为 CSS 已缓存。这可能不会每次都有效(例如,因为某些浏览器可能关闭了缓存),但几乎总是如此。
回答by garlon4
Another way to do this is to check how many style sheets are loaded. For instance:
另一种方法是检查加载了多少样式表。例如:
With "css_filename" the url or filename of the css file, and "callback" a callback function when the css is loaded:
使用“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);
}
回答by gblazex
You either need a specific element which style you know, or if you control the CSS file, you can insert a dummy element for this purpose. This code will exactly make your callback run when the css file's content is applied to the DOM.
您需要一个您知道样式的特定元素,或者如果您控制 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");
});
If you use this code in the bottom of the document, you can move the eland compvariables outside of the timer in order to get the element once. But if you want to attach the handler somewhere up in the document (like the head), you should leave the code as is.
如果在文档底部使用此代码,则可以将el和comp变量移到计时器之外,以便一次性获取元素。但是,如果您想将处理程序附加到文档中的某个位置(如头部),您应该保持代码不变。
Note: tested on FF 3+, IE 5.5+, Chrome
注意:在 FF 3+、IE 5.5+、Chrome 上测试
回答by sje397
This trick is borrowed from the xLazyLoader jQuery plugin:
这个技巧是从xLazyLoader jQuery 插件借来的:
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');
})();
Tested and working locally in FF (3.6.3) and Chrome (linux - 6.0.408.1 dev)
在 FF (3.6.3) 和 Chrome (linux - 6.0.408.1 dev) 中本地测试和工作
Demo here(note that this won't work for cross-site css loading, as is done in the demo, under FF)
Demo here(请注意,这不适用于跨站点 css 加载,就像在演示中所做的那样,在 FF 下)
回答by matsko
The xLazyLoader plugin fails since the cssRules properties are hidden for stylesheets that belong to other domains (breaks the same origin policy). So what you have to do is compare the ownerNode and owningElements.
xLazyLoader 插件失败,因为 cssRules 属性对于属于其他域的样式表是隐藏的(破坏了同源策略)。所以你要做的就是比较 ownerNode 和 owningElements。
Here is a thorough explanation of what todo: http://yearofmoo.com/2011/03/cross-browser-stylesheet-preloading/
以下是对待办事项的详尽解释:http: //yearofmoo.com/2011/03/cross-browser-stylesheet-preloading/
回答by sandstrom
// 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);
});
};

