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.


Related articles: