一、前言
在读layer.js的源码时发现作者用了一个巧妙的方法自动的引入了layer的css,使用者只需要引入layer.js即可,不需要要手动引入css文件。在此也把整个实现方式自己实现了一下,记录和分享。
二、前提
js和需要引入的css的相对路径是需要规定好的, 假设目录如下
|--plugin
|--css
| |--index.css
|--index.js
三、实现这个功能需要解决哪些问题?
- 如何获取css文件的访问路径?
- 如何知道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。可以通过dom的readyState值来判断当前正在解析的是哪个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大概需要以下几个步骤:
- 创建一个带固定id的link标签
- 拼接地址,给link标签的href赋值
- link标签插入到head中
- 判断css标签是否已经加载完成
如何判断css标签是否已经加载完了呢?当
css加载完了, 是会对网页中的元素产生作用的。前面我们生成link标签的时候给它设置了一个固定的id。若加载的css里面根据这个id给link标签设置一个特别的样式,我们只要轮询的去获取此标签对应的样式属性是不是跟设置的一样,就可以判断是否加载完成了。此处我们给这个link标签设置一个width: 2048px,这样既不会影响其他元素的样式,也不会影响布局。因为这个width对link可以说是没用的
上代码:
// 获取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的一个思路和实现过程。这个比较适用于传统的前端开发模式。当下流行的工程化开发几乎不会用到这种方式。但是这样的一个巧妙的实现方式,还是挺有意思的。学会了或许能举一反三,用在其他的地方。