基於某些考慮,有時咱們項目中會盡可能使用原生js
,這種狀況下連最簡單的類選擇器可能都要進行兼容性處理。getElementsByClassName
是後來引入的,歷史不如getElementById
和getElementsByTagName
。越是新的特性,瀏覽器的兼容相對就越差。node
雖然這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; }
至此,就能夠用咱們自定義的類選擇器查找多個類都匹配的標籤了。若是還要完善的話,至少還須要判斷用戶傳入的類名參數是否爲空這種狀況。app
若是還要增強功能的話,能夠考慮實現一個多級選擇器的功能,好比jQuery
中以下的語句,甚至還能夠優化循環遍歷的寫法等。優化
// 選擇id爲app下的全部class名有name的標籤 $("#app .name")
實現一個功能簡單,作成一個產品很難。不過話說回來,若是要自定義太複雜的功能,咱們當初在選擇原生js時就會更加慎重了。spa