如何自动引入css?

1,003 阅读4分钟

一、前言

在读layer.js的源码时发现作者用了一个巧妙的方法自动的引入了layer的css,使用者只需要引入layer.js即可,不需要要手动引入css文件。在此也把整个实现方式自己实现了一下,记录和分享。

二、前提

js和需要引入的css的相对路径是需要规定好的, 假设目录如下

|--plugin
    |--css
    |  |--index.css
    |--index.js

三、实现这个功能需要解决哪些问题?

  1. 如何获取css文件的访问路径?
  2. 如何知道css已经加载完成了?(加载完后可能需要做后续的事情)

四、上菜

1、解决:如何获取css文件的访问路径

获取到此js的src路径,截取除去js文件名的部分,再跟"js和css的相对路径"进行拼接,就可以得到css的路径了。那么如何获取到此js的src路径呢?

知识点1:document上有一个currentScript属性。当浏览器在解析此js的时候,在此js内可以通过document.currentScript.src获取到加载此js的script标签的src

知识点2:ie浏览器的document中没有currentScript。可以通过domreadyState值来判断当前正在解析的是哪个script标签。

上代码:

// 获取此js的src地址
function getScriptPath() {
    let src = '';
    // 当浏览器(ie除外)在加载此js时,document对象上会有一个 currentScript对象
    // currentScript对象就是此js的script标签对象,可从中获取到src的值
    if(document.currentScript) {
        src = document.currentScript.src;
    } else {
        // 浏览器不支持 currentScript属性,可通过dom的readyState来获取此js的dom
        // document.readyState 描述文档的加载状态
        // 分别是 loading(从服务器加载中) interactive(已加载,在解析中,可交互) complete(加载并解析完成)
        // 获取所有的script标签,判断它的 readyState值为interactive的,认为是当前js的script标签
        let scripts = document.scripts;
        let last = scripts.length - 1;
        src = scripts[last].src;
        for(let i = last; i > 0; i--) {
            if(scripts[i].readyState === 'interactive') {
                src = scripts[i].src;
                break;
            }
        }
    }
    return src;
}

2、加载css

加载css大概需要以下几个步骤:

  1. 创建一个带固定id的link标签
  2. 拼接地址,给link标签的href赋值
  3. link标签插入到head中
  4. 判断css标签是否已经加载完成

如何判断css标签是否已经加载完了呢?当css加载完了, 是会对网页中的元素产生作用的。前面我们生成link标签的时候给它设置了一个固定的id。若加载的css里面根据这个idlink标签设置一个特别的样式,我们只要轮询的去获取此标签对应的样式属性是不是跟设置的一样,就可以判断是否加载完成了。此处我们给这个link标签设置一个width: 2048px,这样既不会影响其他元素的样式,也不会影响布局。因为这个widthlink可以说是没用的

上代码:

// 获取dom的指定属性值
function getStyleByAttr(dom, attr) {
    // 获取dom对象的所有样式,style对象。 ie和opera使用dom.currentStyle, 其他浏览器使用 window.getComputedStyle
    let style = dom.currentStyle ? dom.currentStyle : window.getComputedStyle(dom, null);
    // 通过window.getComputedStyle()获取的style, 可通过 getPropertyValue方法获取指定属性值。dom.currentStyle使用 getAttribute方法获取指定属性值
    return style[style.getPropertyValue ? 'getPropertyValue' : 'getAttribute'](attr);
}

function loadCss(href, callback) {
    // 创建一个带id的link标签
    let link = document.createElement('link');
    let linkId = 'my-plugin-css';
    link.id = linkId;
    link.rel = 'stylesheet';
    // 设置link标签的href值
    let src = getScriptPath();
    link.href = src.substring(0, src.lastIndexOf('/') + 1) + href;
    // 插入到head中
    if(!document.getElementById(linkId)) {
        document.getElementsByTagName('head')[0].appendChild(link);
    }
    // 轮询判断css是否加载完成
    (function poll(times) {
        // 超时
        if(times > 100) {
            return window.console && window.console.error(linkId + ' 加载失败');
        }
        // 在要加载的css中给此处生成的link添加一个width:20210428px的样式
        // 根据id去获取此link标签,并且获取此标签的width进行对比,若值相等,认为加载完成
        let linkDom = document.getElementById(linkId);
        if(parseInt(getStyleByAttr(linkDom, 'width')) === 2048) {
            callback();
        } else {
            setTimeout(function() {
                poll(++times)
            }, 100)
        }
    })(1);
}

loadCss('css/index.css', function(){
    console.log('css加载完成');
})

五、总结

以上是js自动引入css的一个思路和实现过程。这个比较适用于传统的前端开发模式。当下流行的工程化开发几乎不会用到这种方式。但是这样的一个巧妙的实现方式,还是挺有意思的。学会了或许能举一反三,用在其他的地方。