JS DOM操作(2) 事件(1)

1、寫一個(gè)函數(shù),批量操作 css

function css(node, styleObject){
    for(var key in styleObject){
        node.style[key] = sytleObject[key]
    }
}
css(document.body, {
  'color': 'red',
  'background-color': '#ccc'
})

2、如何獲取 DOM 計(jì)算后的樣式

//css
div {
  color: red;
  fontsize: 20px;
}
//html
<div id="name">海山城</div>

設(shè)置div的background為pink

document.querySelector("#name").style.background = 'pink'

檢查代碼發(fā)現(xiàn)相當(dāng)于做了這樣一件事,相當(dāng)于添加了行內(nèi)樣式

<div id="name" style="background: pink;">海山城</div>

因此如果行內(nèi)樣式?jīng)]有某個(gè)屬性,通過(guò)這個(gè)方式是獲取不到該屬性對(duì)應(yīng)的值的

//css
div {
  color: red;
  fontsize: 20px;
}
//html
<div id="name" style="background:blue">海山城</div>
//JS
var background = document.querySelector("#name").style.background
var fontsize = document.querySelector("#name").style.fontsize
console.log(background)    //"blue"
console.log(fontsize)      //"",獲取不到

那么我就想獲取到某個(gè)元素的非行內(nèi)樣式怎么辦呢?
getcomputedStyle:獲取元素計(jì)算后的樣式,不要通過(guò) node.style.屬性 獲取

//css
div {
  color: red;
  fontsize: 20px;
}
//html
<div id="name">海山城</div>
//JS
var color = window.getComputedStyle(document.querySelector("#name")).color
var fontsize= window.getComputedStyle(document.querySelector("#name"))['font-size']
//或者var fontsize= window.getComputedStyle(document.querySelector("#name")).fontSize
console.log(color)
console.log(fontsize)

注: window.getComputedStyle()得到的是一個(gè)存放了所有計(jì)算后的樣式的對(duì)象,因此 . 和 [] 都可以訪問(wèn)其中所有的屬性樣式,[]中用的是css寫法(font-size),想用 . 可以使用駝峰(fontSize)

3、實(shí)現(xiàn)此效果

通過(guò)node.style.屬性添加樣式太麻煩,需要一條一條的添加??梢詫⑺行枰砑拥膶傩詫懙揭粋€(gè)class中,那個(gè)元素需要這些樣式,直接給這個(gè)元素添加這個(gè)class就行了
代碼實(shí)現(xiàn)

4、onlick與addEventListener的區(qū)別?

用法:

//onclick
btn.onclick = function(){
   console.log("第一")
}
// "第一"
//addEventListener
var btnClick = document.getElementById('btnClick');
btnClick.addEventListener('click', function() {
    console.log("第一")
}, false)
btnClick.addEventListener('click', function() {
    console.log("第二")
}, false)
/* "第一"
   "第二" */

區(qū)別:

  • 名稱的區(qū)別。一個(gè)是onclick,而addEventListener中的參數(shù)是click
  • onclick只可以綁定一個(gè)事件,而addEventListener可以綁定多個(gè)事件
    btn.onclick = function(){console.log("第一")}相當(dāng)于對(duì)一個(gè)對(duì)象賦值,那么在btn.onclick = function(){console.log("第二")}就會(huì)把前面的值覆蓋,所以onclick不能綁定多個(gè)事件
  • onclick只能是在默認(rèn)的冒泡階段,而addEventListener可以指定參數(shù)來(lái)設(shè)置是捕獲階段還是冒泡階段,第三個(gè)參數(shù)可以不寫(即為false),代表的是默認(rèn)的冒泡階段,如果是true則代表的是捕獲階段

5、解釋DOM2事件傳播機(jī)制?

首先了解下什么是事件流?
事件流描述的是從頁(yè)面中接收事件的順序,比如有兩個(gè)嵌套的div,點(diǎn)擊了內(nèi)層的div,這時(shí)候是內(nèi)層的div先觸發(fā)click事件還是外層先觸發(fā)?目前主要有三種模型

  • IE的事件冒泡:事件開(kāi)始時(shí)由最具體的元素接收,然后逐級(jí)向上傳播到較為不具體的元素


  • Netscape的事件捕獲:不太具體的節(jié)點(diǎn)更早接收事件,而最具體的元素最后接收事件,和事件冒泡相反


  • DOM2級(jí)事件流:包括三個(gè)階段,事件捕獲階段,處于目標(biāo)階段,事件冒泡階段,首先發(fā)生的是事件捕獲,為截取事件提供機(jī)會(huì),然后是實(shí)際目標(biāo)接收事件,最后是冒泡階段

再來(lái)看個(gè)DOM2事件傳播機(jī)制的有趣事例
事例代碼地址
代碼中嵌套了3個(gè)div,并且為每個(gè)div都綁定了對(duì)應(yīng)的捕獲以及冒泡事件。當(dāng)我點(diǎn)擊div#inner時(shí),控制臺(tái)的結(jié)果為:

outer捕獲
middle捕獲
inner冒泡
inner捕獲
middle冒泡
outer冒泡

what?為啥inner冒泡會(huì)在inner捕獲前面?。??
原理:DOM事件觸發(fā)分為三個(gè)階段
①、事件捕獲階段,即由最頂層元素(一般是從window元素開(kāi)始,有的瀏覽器是從document開(kāi)始)開(kāi)始,逐次進(jìn)入dom內(nèi)部,最后到達(dá)目標(biāo)元素,依次執(zhí)行綁定在其上的事件(不包括目標(biāo)元素上的事件)
②、處于目標(biāo)階段,檢測(cè)機(jī)制到達(dá)目標(biāo)元素,按事件注冊(cè)順序執(zhí)行綁定在目標(biāo)元素上的事件。
③、事件冒泡階段,從目標(biāo)元素出發(fā),向外層元素冒泡,最后到達(dá)頂層(window或document),依次執(zhí)行綁定再其上的事件(不包括目標(biāo)元素上的事件)。
所以,div#inner上的事件是處于目標(biāo)階段的事件,會(huì)按事件注冊(cè)順序執(zhí)行,和設(shè)定的是捕獲還是冒泡事件并無(wú)關(guān)系

☆6、有如下代碼,要求當(dāng)點(diǎn)擊每一個(gè)元素li時(shí)控制臺(tái)展示該元素的文本內(nèi)容。不考慮兼容

<ul class="ct">
    <li>這里是</li>
    <li>海山城</li>
    <li>大世界</li>
</ul>
<script>
//todo ...
</script>

方法一:利用this。this指代的就是綁定事件的那個(gè)元素

var liList = document.getElementsByTagName('li')
for(var i=0; i<liList.length; i++){
  liList[i].onclick = function(){
    console.log(this.innerText)
  }
}

方法二:利用閉包。

var liList = document.getElementsByTagName('li')
for(var i=0; i<liList.length; i++){
  (function(new_i){
    liList[new_i].onclick = function(){
      console.log(liList[new_i].innerText)
    }
  })(i)
}

那么,問(wèn)題來(lái)了,我不加立即執(zhí)行函數(shù)生成閉包不行嗎,就像下面這樣

var liList = document.getElementsByTagName('li')
for(var i=0; i<liList.length; i++){
  liList[i].onclick = function(){
    console.log(liList[i].innerText)//如果console.log(i),點(diǎn)擊任何li都會(huì)輸出3
  }
}

點(diǎn)擊每個(gè)li會(huì)發(fā)現(xiàn)控制臺(tái)報(bào)錯(cuò),什么原因呢?
原理:

  • onclick的回調(diào)函數(shù)會(huì)被加入到任務(wù)隊(duì)列中,等主線程所有代碼執(zhí)行完畢,才開(kāi)始執(zhí)行。因此,循環(huán)會(huì)先被執(zhí)行完,最后得到i的值為length(我這里只有3個(gè)li,i也就是3),最后無(wú)論點(diǎn)擊哪個(gè)li時(shí),對(duì)應(yīng)的回調(diào)函數(shù)執(zhí)行console.log(liList[3].innerText),已經(jīng)越界了,所以會(huì)報(bào)錯(cuò)!!!
  • 如果給代碼塊包裝一個(gè)立即執(zhí)行函數(shù),并把i作為參數(shù)傳入的話
    相當(dāng)于var new_i = i,console.log(liList[new_i ].innerText。那么后續(xù)i++對(duì)當(dāng)前new_i并沒(méi)有影響

方法三:事件代理(推薦?。。?,不對(duì)每個(gè)節(jié)點(diǎn)都設(shè)置事件,對(duì)其父節(jié)點(diǎn)設(shè)置事件

var ul = document.querySelector('.ct')
ul.addEventListener('click',function(e){
   console.log(e.target.innerText)
})
  • 在事件處理程序內(nèi)部,this始終等同于currentTarget(綁定事件的目標(biāo)),而target是事件的實(shí)際目標(biāo)(實(shí)際點(diǎn)擊的目標(biāo))
  • e是指event事件對(duì)象,包含了創(chuàng)建它的特定事件有關(guān)的屬性和方法,preventDefault(),stopPropagation(),target這三個(gè)最為重要
  • e是哪邊來(lái)的呢?在addEventListener的內(nèi)部var event = {},然后系統(tǒng)收集到的方法屬性會(huì)被添加到對(duì)象中,最后callback(event)執(zhí)行,也就是把event作為參數(shù)傳入到了callback中,所以上述代碼等價(jià)于:
var ul = document.querySelector('.ct')
ul.addEventListener('click',function(){
   console.log(arguments[0].target.innerText)//addEventListener函數(shù)的內(nèi)部會(huì)將event對(duì)象作為第一個(gè)參數(shù)傳入call函數(shù)中
})

7、補(bǔ)全代碼

要求:

  • 當(dāng)點(diǎn)擊按鈕開(kāi)頭添加時(shí)在<li>這里是</li>元素前添加一個(gè)新元素,內(nèi)容為用戶輸入的非空字符串;當(dāng)點(diǎn)擊結(jié)尾添加時(shí)在最后一個(gè) li 元素后添加用戶輸入的非空字符串.
  • 當(dāng)點(diǎn)擊每一個(gè)元素li時(shí)控制臺(tái)展示該元素的文本內(nèi)容。
<ul class="ct">
    <li>這里是</li>
    <li>饑人谷</li>
    <li>任務(wù)班</li>
</ul>
<input class="ipt-add-content" placeholder="添加內(nèi)容"/>
<button id="btn-add-start">開(kāi)頭添加</button>
<button id="btn-add-end">結(jié)尾添加</button>
<script>
//你的代碼
</script>

我的實(shí)現(xiàn)

8、補(bǔ)全代碼

要求:當(dāng)鼠標(biāo)放置在li元素上,會(huì)在img-preview里展示當(dāng)前l(fā)i元素的data-img對(duì)應(yīng)的圖片。

<ul class="ct">
    <li data-img="1.png">鼠標(biāo)放置查看圖片1</li>
    <li data-img="2.png">鼠標(biāo)放置查看圖片2</li>
    <li data-img="3.png">鼠標(biāo)放置查看圖片3</li>
</ul>
<div class="img-preview"></div>
<script>
//你的代碼
</script>

我的實(shí)現(xiàn)

參考
關(guān)于DOM2級(jí)事件的事件捕獲和事件冒泡

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 作者:codeXiu 來(lái)源:掘金 事件流 事件流一共由三個(gè)階段分別是: 1.捕獲階段 2.目標(biāo)階段 3.冒泡階段 ...
    強(qiáng)哥科技興閱讀 551評(píng)論 0 1
  • 聲明:本文來(lái)源于http://www.webzsky.com/?p=731我只是在這里作為自己的學(xué)習(xí)筆記整理一下(...
    angryyan閱讀 7,247評(píng)論 1 6
  • 事件:交互體驗(yàn)的核心,div.onclick = function(){};// "="號(hào)左邊就是事件了,右邊是觸...
    官清歲月閱讀 437評(píng)論 0 1
  • 1. 事件冒泡與事件捕獲 事件冒泡和事件捕獲分別由微軟和網(wǎng)景公司提出,這兩個(gè)概念都是為了解決頁(yè)面中事件流(事件發(fā)生...
    cbw100閱讀 2,810評(píng)論 0 8
  • 以前總感覺(jué)三十而立很遠(yuǎn),轉(zhuǎn)眼間已經(jīng)到了而立之年。到了而立之年時(shí),面臨的是更種的困惑,該拿出什么來(lái)立? 前些天在微博...
    MrGowiy閱讀 478評(píng)論 0 0

友情鏈接更多精彩內(nèi)容