ul中有li,點選li,獲得對應的序號

小小小小卓發表於2018-09-12

ul中有li,點選li,獲得對應的序號

Happy coding...

作為一個前端開發者,一定會遇到這樣的一個問題:

一個<ul>中裡面巢狀了好多空的<li>標籤,點選<li>彈出它的序號(HTML不可以修改)。即點選第一個<li>標籤時彈出0,點選第二個<li>標籤時彈出1。HTML的結構如下:

<ul id="zhuo">
    <li>1</li>
    <li>2</li>
    <li>3</li>
</ul>
複製程式碼

我們一看到這樣的需求及HTML結構時,第一時間就會想著用for+onclick+console.log來完成這個事情:

var zhuo = document.getElementById('zhuo')
var li = zhuo.getElementsByTagName('li')

for(var i = 0, len = li.length; i < len; ++i){
    li[i].onclick = function () {
        console.log(i) // 寫到這裡,心中暗爽:不就這麼簡單嗎?那你這坑就踩得穩穩的,一測試發現
    }
}
// 無論你點選哪一個標籤都會輸出:3
// 這是為什麼呢?
// 下面分析一下程式碼。

// console.log(i) // 假裝沒有被註釋
複製程式碼
  1. for迴圈時,我們給每一個li註冊了一個click事件,每一個li被點選的時候應該彈出它的序號i
  2. 然而,事實是當你點選li的時候,for迴圈早已經執行完,已經給每個li註冊了事件
  3. 此時,for迴圈已經遍歷完,i的值已經變成li.length(問題就出現在這裡)
  4. 所以,等到你再去點選li時,彈出的是3,而不是它對應的序號
  5. 造成這個問題的根本:JS在ES6之前,沒有塊級作用域的概念,於是這裡的i本應在迴圈結束後就被銷燬,但,這裡並沒有!

沒有塊級作用域,那麼我們給它偽裝一個不就行了?那麼問題來了:{}並不能代表一個作用域,那麼用什麼來解決這個問題呢?

相信聰明的你已經想到了,用一等公民的function,函式擁有自己的作用域;

var zhuo = document.getElementById('zhuo')
var li = zhuo.getElementsByTagName('li')

for (var i = 0, len = li.length; i < len; ++i) {
  (function (j) {// 什麼?你問我這個是什麼意思?
	// 這是一個閉包函式(IIFE),立即執行函式
    // 接收i的值儲存在j中,這個j會一直儲存在這個函式作用域
    li[j].onclick = function () {
      console.log(j)
    }

  })(i)// 這裡可以看成函式的入口,傳入i
}
複製程式碼

什麼?你說這個太燒腦?那好吧,我講演示個簡單的:

var zhuo = document.getElementById('zhuo')
var li = zhuo.getElementsByTagName('li')

for (var i = 0, len = li.length; i < len; ++i) {
    
  li[i].index = i // 因為這個li是一個dom物件,所以我們可以在它上面新增屬性
  li[i].onclick = function () {
    var index = this.index // 這裡的this指向li[i],因此我們可以從上面取到之前的index屬性值
    console.log(index)
  }

}
// 終於寫完了....
複製程式碼

納尼?你說,this的指向太難理解?你怎麼就這麼難伺候叻。。。

好吧,我再和你說一個**“爸爸給兒子幹活”**的做法:

  1. 你(li)小時候,想帶個玩具(index)去跟小朋友玩(炫耀);
  2. 你突然就發現,那個玩具被你上次扔到衣櫃上去了,你又拿不到;
  3. 於是,你會大聲叫爸爸(ul)/大舅(div)/爺爺(body
  4. 然後,你爸/大舅/爺爺聽到你的聲音後,就會過來問你,發生什麼事(打一頓)?
  5. 接著,他知道是你想叫他過來拿這個玩具,好出去玩
  6. 他給你拿下來後,你就屁顛屁顛(屁股上兩巴掌印)地出去玩了

這一整個流程就是,DOM中的冒泡機制(1,2,3)與捕獲機制(4,5,6),這也就是我們接下來要說的委託事件原理

var ul = document.getElementById('zhuo')

ul.onclick = function(event){
  event = event || window.event // 額...理解不了的話,你當這個是一個DOM物件
  target = event.target // 獲得點選的最底層DOM

  if(target.nodeName === 'LI'){// 判斷這個DOM節點名字是不是li,
    console.log(target.innerHTML)// 好了,被你發現了;這裡不是彈出序號,是內容。
  }
}
// 聰明的同學一定看出來了,這樣的寫法更簡潔,不需要for迴圈,不需要給每個li繫結click事件
// 這樣做的好處多多
// 誒誒誒,你別走啊,都看到這裡了,接著看下去唄。
// 下面的辦法更簡單
複製程式碼

好了,看到這裡,你會疑惑為什麼是ES6之前沒有,難道現在就有了?

沒錯,在前端開發的歷(ku)史(bi)長(ri)河(zi)中,開發者們越來越覺得不對勁,於是就在ES6加入了牛逼哄哄的let

let到底是何方神聖,為什麼博主在這裡說它?他?她?牛逼呢?

let允許你宣告一個作用域被限制在塊級中的變數、語句或者表示式。與var關鍵字不同的是,它宣告的變數只能是全域性或者整個函式塊的。

也就是說,用這個let,我們就可以很nice地解決上面提到的問題了

var zhuo = document.getElementById('zhuo')
var li = zhuo.getElementsByTagName('li')

for(let i = 0, len = li.length; i < len; ++i){
    li[i].onclick = function () {
        console.log(i) // 噹噹噹,是不是覺得So easy?
    }
}
// 什麼?你的開發環境不支援ES6的語法?
// 告辭!打擾了
// 我不跟chrome都沒升級到最新版的人交朋友
// 再見!
複製程式碼

都看完了,也不評論一下,點個贊嗎?

相關文章