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

基于某些考虑,有时咱们项目中会尽可能使用原生js,这种状况下连最简单的类选择器可能都要进行兼容性处理。getElementsByClassName是后来引入的,历史不如getElementByIdgetElementsByTagName越是新的特性,浏览器的兼容相对就越差。node

虽然这3个选择器都并非百分百兼容全部浏览器,好比getElementByIdgetElementsByTagNameIE上只支持>=5.5,不过谁还用低于5.5IE呢?但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;
}

至此,就能够用咱们自定义的类选择器查找多个类都匹配的标签了。若是还要完善的话,至少还须要判断用户传入的类名参数是否为空这种状况。app

若是还要增强功能的话,能够考虑实现一个多级选择器的功能,好比jQuery中以下的语句,甚至还能够优化循环遍历的写法等。优化

// 选择id为app下的全部class名有name的标签
$("#app .name")

实现一个功能简单,作成一个产品很难。不过话说回来,若是要自定义太复杂的功能,咱们当初在选择原生js时就会更加慎重了。spa

图片描述

相关文章
相关标签/搜索