Implementation and in depth analysis of dynamic loading css method
- 2021-07-12 04:47:13
- OfStack
1. Method citation source and application
This dynamic loading css method, loadCss, is stripped from Sea. js, and is further optimized (the optimization code will be analyzed later).
Because company projects need lazy loading to improve the loading speed of websites, css files that are not necessary for first screen rendering are dynamically loaded.
2. Complete code after optimization
/*
* @function Dynamic loading css Documents
* @param {string} options.url -- css Resource path
* @param {function} options.callback -- Post-load callback function
* @param {string} options.id -- link Label id
*/
function loadCss(options){
var url = options.url,
callback = typeof options.callback == "function" ? options.callback : function(){},
id = options.id,
node = document.createElement("link"),
supportOnload = "onload" in node,
isOldWebKit = +navigator.userAgent.replace(/.*(?:AppleWebKit|AndroidWebKit)\/?(\d+).*/i, "$1") < 536, // webkit The old kernel does special processing
protectNum = 300000; // Threshold 10 Minutes, 1 Execute in seconds pollCss 500 Times
node.rel = "stylesheet";
node.type = "text/css";
node.href = url;
if( typeof id !== "undefined" ){
node.id = id;
}
document.getElementsByTagName("head")[0].appendChild(node);
// for Old WebKit and Old Firefox
if (isOldWebKit || !supportOnload) {
// Begin after node insertion
setTimeout(function() {
pollCss(node, callback, 0);
}, 1);
return;
}
if(supportOnload){
node.onload = onload;
node.onerror = function() {
// Load failure (404)
onload();
}
}else{
node.onreadystatechange = function() {
if (/loaded|complete/.test(node.readyState)) {
onload();
}
}
}
function onload() {
// Make sure you only run 1 Secondary download operation
node.onload = node.onerror = node.onreadystatechange = null;
// Empty node Reference, in lower version IE Failure to clear will cause memory leakage
node = null;
callback();
}
// Cyclic judgment css Has it been loaded successfully
/*
* @param node -- link Node
* @param callback -- Callback function
* @param step -- Pedometer to avoid infinite circulation
*/
function pollCss(node, callback, step){
var sheet = node.sheet,
isLoaded;
step += 1;
// Protection, greater than 10 Minutes, polling is no longer performed
if(step > protectNum){
isLoaded = true;
// Empty node Quote
node = null;
callback();
return;
}
if(isOldWebKit){
// for WebKit < 536
if(sheet){
isLoaded = true;
}
}else if(sheet){
// for Firefox < 9.0
try{
if(sheet.cssRules){
isLoaded = true;
}
}catch(ex){
// Firefox special version, through a specific value to know whether the download was successful
// The value of `ex.name` is changed from "NS_ERROR_DOM_SECURITY_ERR"
// to "SecurityError" since Firefox 13.0. But Firefox is less than 9.0
// in here, So it is ok to just rely on "NS_ERROR_DOM_SECURITY_ERR"
if(ex.name === "NS_ERROR_DOM_SECURITY_ERR"){
isLoaded = true;
}
}
}
setTimeout(function() {
if(isLoaded){
// Delay 20ms To allow enough rendering time for downloaded styles
callback();
}else{
pollCss(node, callback, step);
}
}, 20);
}
}
3. Parse the code
1. Parameters
This method supports three parameters and can be extended.
1.1 opations.url
url is the css resource path that needs to be introduced, that is, the href attribute content of the tag.
1.2 options.id
id is the id attribute of the tag. This parameter is unnecessary and may not be passed. The main function is to mark the current label, which is convenient for js to find and determine whether an css file has been loaded.
1.3 options.callback
callback is the callback function that is called after the css file is loaded. There are also special scenarios where the file fails to load and the callback function is still executed.
2. Generate the tag and insert the header head to load the resource
var url = options.url,
callback = typeof options.callback == "function" ? options.callback : function(){},
id = options.id,
node = document.createElement("link");
node.rel = "stylesheet";
node.type = "text/css";
node.href = url;
if( typeof id !== "undefined" ){
node.id = id;
}
document.getElementsByTagName("head")[0].appendChild(node);
Generate an dom node, and then configure the necessary attribute values such as rel, type and href, so that the browser can parse the linked resources normally.
Next, find the head node and insert the node.
3. pollCss method to monitor the download status of css resources
The responsibility of the pollCss method is to determine whether the inserted link node, or node variable feedback resource, has been loaded.
3.1 Main basis for judgment
When the browser loads css resources, it will generate sheet attributes for the link node. According to different browsers, you can read the related contents of sheet attributes to judge whether the loading has been completed. So the first sentence var sheet = node. sheet the first thing to do is to get the sheet attribute value.
3.2 Normal Browser Judgment
try{
if(sheet.cssRules){
isLoaded = true;
}
}catch(ex){
// Firefox special version, through a specific value to know whether the download was successful
// The value of `ex.name` is changed from "NS_ERROR_DOM_SECURITY_ERR"
// to "SecurityError" since Firefox 13.0. But Firefox is less than 9.0
// in here, So it is ok to just rely on "NS_ERROR_DOM_SECURITY_ERR"
if(ex.name === "NS_ERROR_DOM_SECURITY_ERR"){
isLoaded = true;
}
}
If reading sheet. cssRules has a value, it proves that the css resource has been linked into the page and parsing begins. At this point, it can be judged that the resource was loaded successfully.
If the reading fails, it is judged whether there is a specific name attribute ex. name === "NS_ERROR_DOM_SECURITY_ERR" according to the wrong content. If it exists, it means that it is a lower version of Firefox (before 9.0) and the resource has been loaded successfully.
3.3 Old webkit kernel browser judgment
var isOldWebKit = +navigator.userAgent.replace(/.*(?:AppleWebKit|AndroidWebKit)\/?(\d+).*/i, "$1") < 536; // webkit The old kernel does special processing
if(isOldWebKit){
// for WebKit < 536
if(sheet){
isLoaded = true;
}
}
If it is an old kernel browser of webkit, it is only necessary to judge that the attribute value of sheet exists, which means that the resource is loaded.
3.4 Add multiple loop detection
setTimeout(function() {
if(isLoaded){
// Delay 20ms To allow enough rendering time for downloaded styles
callback();
}else{
pollCss(node, callback, step);
}
}, 20);
After the pollCss method is triggered, the sheet value may not be detected for the first time. That means it hasn't been loaded yet. So polling is needed. Here, an inquiry is conducted every 20ms until the resource load is completed.
3.5 Polling fault tolerance (optimization for Sea. js source code)
There are also opportunities for css resource loading to go wrong, and there is a possibility that the onerror method will not be triggered. Without a protection of 1, the polling may last for 1 straight, so a limit threshold is required.
var protectNum = 300000, // Threshold 10 Minutes, 1 Execute in seconds pollCss 500 Times
step = 0;
// A lot of code ....
step += 1;
// Protection, greater than 10 Minutes, polling is no longer performed
if(step > protectNum){
isLoaded = true;
// Empty node Quote
node = null;
callback();
return;
}
The threshold here is polling for 10 minutes. If the condition is still not met after 10 minutes, the default resource has been downloaded, the callback method is executed, and the node reference is emptied.
4. Determine when to trigger an pollCss check
4.1 Application Scenarios for pollCss Polling
Use pollCss for polling when the browser kernel is an older webkit kernel or when nodes are not supported to trigger onload methods.
// for Old WebKit and Old Firefox
if (isOldWebKit || !supportOnload) {
// Begin after node insertion
setTimeout(function() {
pollCss(node, callback, 0);
}, 1);
return;
}
5. Modern browsers use onload and onreadystatechange directly to judge
Modern browsers judge in this way, which can avoid the disadvantages of polling. The judgment is more accurate and timely.
5.1 onload method
function onload() {
// Make sure you only run 1 Secondary download operation
node.onload = node.onerror = node.onreadystatechange = null;
// Empty node Reference, in lower version IE Failure to clear will cause memory leakage
node = null;
callback();
}
After the onload method is triggered, multiple related methods should be reset immediately to avoid multiple triggers of callback.
node = null; The purpose of resetting node to null is to avoid the memory overflow problem of the lower version of IE and clear the useless dom nodes in time.
Finally, the callback method is executed.
5.2 Support for onload Method Browser Handling
if(supportOnload){
node.onload = onload;
node.onerror = function() {
// Load failure (404)
onload();
}
}
5.3 Processing of onload Method Browser is not supported
if(supportOnload){
// Code ...
}else{
node.onreadystatechange = function() {
if (/loaded|complete/.test(node.readyState)) {
onload();
}
}
}
4. Postscript
The reason for choosing the method of stripping Sea. js for transformation: Because the js library is widely used, if there is a problem, the author will repair it in time. Therefore, taking this code as a blueprint for transformation fits the company's user base and avoids large-scale problems.
After applying this method to products, up to now, there is no abnormal style feedback from customers. Therefore, Cheng Xuyuan who read this article can use it with confidence.
ps: The company has a user base of more than 10 million users, involving large and small browsers, ranging from IE6 to chrome.