達文西,用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

圖片描述

相關文章
相關標籤/搜索