达文西,用JS写个兼容IE8浏览器的类选择器

20次阅读

共计 2516 个字符,预计需要花费 7 分钟才能阅读完成。

基于某些考虑,有时我们项目中会尽量使用原生 js,这种情况下连最简单的类选择器可能都要进行兼容性处理。getElementsByClassName 是后来引入的,历史不如 getElementById 和 getElementsByTagName。越是新的特性,浏览器的兼容相对就越差。
虽然这 3 个选择器都并不是百分百兼容所有浏览器,比如 getElementById 和 getElementsByTagName 在 IE 上只支持 >=5.5,不过谁还用低于 5.5 的 IE 呢?但 getElementsByClassName 就不同了,它在 IE 上只支持 >=9,所以就存在兼容性的问题。
兼容的方式,就是利用 getElementsByTagName 来获取所有的标签,然后判断每个标签有没有 class,以及 class 里面的值是不是等于我们要找的。《JavaScript DOM 编程艺术(第 2 版)》第 42 页有一个简单实现,但因为作者只是想说明原理,所以没有完善,用了 indexOf 去判断我们要的类名在不在标签的类名中,这会导致假如我们要找 nam 的话会把类名叫 name 的都找出来。所以网上有很多的实现,大致如下,并且下面的实现还考虑了标签的类名可能有多个类的情况。
<div id=”app”>
<p>zero</p>
<p class=”name name-one”>one</p>
<p class=”name name-one name-two”>two</p>
<p class=”name name-one name-two name-three”>three</p>
</div>

<script>
function getElementsByClassName(node, className){
// 如果支持原生 getElementsByClassName 就直接使用并返回结果
if (node.getElementsByClassName){
return node.getElementsByClassName(className);
}
// 这是最终返回的结果数组
var results = new Array();
// 先获取 node 节点下所有的标签
var elements = node.getElementsByTagName(“*”);
// 循环遍历获得的所有标签
for (var i = 0; i < elements.length; i++){
// 获取循环中的标签
var ele = elements[i];
// 获取该标签的类名
var cName = ele.className;
// 如果类名为空,也就是没有 class,那么这个标签肯定不是,所以继续循环下一次标签
if (cName === “”){
continue;
}
// 如果是多个 class,那么就分别获得这几个 class
var cNames = cName.split(” “);
// 循环遍历标签中的几个 class,只要有一个 class 和我们要的 className 相等,说明就是匹配的标签
for (var j = 0; j < cNames.length; j++){
if (cNames[j] === className){
results[results.length] = ele;
break;
}
}
}
return results;
}

// 使用自定义的类选择器
var nodes = getElementsByClassName(document.getElementById(“app”), “name-three”);
for (var i = 0; i < nodes.length; i++){
console.log(nodes[i].innerText);
}
</script>
如果在网络上找类似的实现的话,基本上就是到上面这一步。但上面的实现仍然存在一个缺陷,比如要选择类名既包括 name 又包括 name-three 的标签就没法实现。
var nodes = getElementsByClassName(document.getElementById(“app”), “name name-three”);
但原生的 getElementsByClassName 是支持多个类名选择的,既然要写一个兼容的自定义类选择器代替原生的,那么这个功能说什么也要上啊。和上面的变化,主要在于我们不仅要处理每个标签可能有多个类名的情况,也要处理我们传入的类名参数可能也是多个类名组成的情况,所以用两层循环可以实现,这里只给出与上面不同的代码部分。
// 标签:如果是多个 class,那么就分别获得这几个 class
var cNames = cName.split(” “);
// 我们要找的类名:如果是多个 class,那么就分别获得这几个 class
var classNames = className.split(” “);

// 设置一个标记,默认为 true,如果在循环判断中发现有条件不满足,设置为 false
var flag = true;
// 先循环我们要找的每一个类名
for (var j = 0; j < classNames.length && flag; j++){
// 看看我们的这个类名在不在这个标签的所有类名中
for (var k = 0; k < cNames.length; k++){
if (classNames[j] === cNames[k]){
break;
}else if(classNames[j] !== cName[k] && k === cNames.length – 1){
// 循环到标签最后一个类名了,还不相等,就说明不匹配
flag = false;
break;
}
}
}

// 如果符合条件,就加入结果集然后返回
if (flag){
results[results.length] = ele;
}
至此,就可以用我们自定义的类选择器查找多个类都匹配的标签了。如果还要完善的话,至少还需要判断用户传入的类名参数是否为空这种情况。
如果还要加强功能的话,可以考虑实现一个多级选择器的功能,比如 jQuery 中如下的语句,甚至还可以优化循环遍历的写法等。
// 选择 id 为 app 下的所有 class 名有 name 的标签
$(“#app .name”)
实现一个功能简单,做成一个产品很难。不过话说回来,如果要自定义太复杂的功能,我们当初在选择原生 js 时就会更加慎重了。

正文完
 0