前端筆記知識點整合之JavaScript(十)深入JavaScript節點&DOM&事件

来源:https://www.cnblogs.com/rope/archive/2019/03/27/10610253.html
-Advertisement-
Play Games

一、DOM JavaScript語言核心。變數的定義、變數的類型、運算符、表達式、函數、if語句、for迴圈、演算法等等。這些東西都屬於語言核心,下次繼續學習語言核心就是面向對象了。JavaScript能做非常多的事情:DOM開發、Ajax開發、Canvas開發、NodeJS開發、前端框架(React ...


DOM

JavaScript語言核心。變數的定義、變數的類型、運算符、表達式、函數、if語句、for迴圈、演算法等等。這些東西都屬於語言核心,下次繼續學習語言核心就是面向對象了。JavaScript能做非常多的事情:DOM開發、Ajax開發、Canvas開發、NodeJS開發、前端框架(ReactVueAngular等等)、HTML5開發。這些都需要語言核心的知識。

DOM開發說白了就是瀏覽器中的頁面效果開發,在2011年之前,DOM開發占據了前端開發工程師的90%的工作;但是現在,DOM開發的工作比重已經降到了10%以下。換句話說,2011年之前,前端 = 做特效的;2011年之後,前端要負責得到後臺的數據介面,用前端MVC邏輯分層開發前端組建、界面、功能,還要寫HTML5,還要做canvas動畫!

上層的框架屏蔽了下層的語言的一些麻煩、不方便的東西,並且提供更方便的API。

jQuery就是乾這個事情的,把JS中的不方便封裝起來,暴露的API都是非常簡便的。

jQuery的哲學就是DOM編程領域的霸主,操作DOM節點、綁定監聽、運動、css樣式、Ajax等等都有封裝。

工作上都是用jQuery,如果不用jQuery也是用類似的東西。沒有人會不用輪子去開發頁面效果。

 

JavaScript中Library表示“庫”,如果這個庫的功能很強大,甚至顛覆了傳統編程的語法、行文習慣,我們就可以叫做“框架”。


1.1 DOM是什麼

文檔對象模型 (DOMDocument Object Model) HTML XML 文檔的編程介面。它給文檔(結構樹)提供了一個結構化的表述並且定義了一種方式—程式可以對結構樹進行訪問,以改變文檔的結構,樣式和內容。 DOM 提供了一種表述形式— 將文檔作為一個結構化的節點組以及包含屬性和方法的對象。從本質上說,它將 web 頁面和腳本或編程語言連接起來了

到底什麼是DOM就是你可以像操作對象一樣操作HTML頁面,而不是操作字元串。

DOMweb 頁面和腳本或編程語言連接起來了。

回看一下我們之前學習的DOM操作,都在幹嘛?我們在開發特效,但是微觀的看,實際上在進行:

1) 得到HTML節點

2) 改變節點的屬性

3) 改變節點的樣式

4) 修改節點、刪除節點、增加節點

5) 節點之間的關係


1.2原生JavaScript得到節點

 document.getElementById('box');

 document.getElementsByTagName('p');

以上兩是全線瀏覽器都相容的得到元素方法。

 

以下這些得到元素的方法都不相容IE678 

document.getElementsByName('aaa')[0]  //通過name屬性得到元素們
document.getElementsByClassName('pp') //通過類名得到元素們
document.querySelector('#box');
document.querySelectorAll('#box p');  //通過選擇器得到元素們

jQueryDOM開發的王者!幫我們解決了元素選擇的相容問題。

jQuery底層很強大,比如$('.par1')機制不是getElementsByClassName(),而是在遍歷所有節點,選擇類名有par1的項。

 


二、原生JavaScript節點關係

jQuery中學習parent()children() siblings()next()prev()等等節點關係,JS中也有對應的屬性。

原生JS提供的節點關係很少:

childNodesfirstChildlastChildparentNodenextSiblingpreviousSibling

 

常見的nodeType值:

 1-普通元素節點、比如divp等等

 2-屬性節點

 3-文本節點

 8-註釋節點

 9-document節點

 10-文檔DTD

 

想要查看某一個元素的節點類型,直接讀取它的nodeType屬性即可。

改變nodeType3的文本節點的內容,要改變他的nodeValue屬性

 

2.1 childNodes兒子節點

childNodesIE678和高級瀏覽器不一致,高級瀏覽器認為所有的換行為空文本節點,而IE678無視這個空文本節點。

div中沒有文本節點,此時應該是4個節點,但是IE9Chrome、火狐會認為有9個節點、IE8認為有4個節點。

高級瀏覽器會把空文本當做一個節點,標簽前後的空文本也被算作一個。

註釋的前後算不算空文本節點,各個瀏覽器有不同的解釋。所以用節點的時候,一定要去過濾、判斷節點的nodeType是不是1

<div id="box">
    <p></p>
    <p></p>
    <p></p>
    <p></p>
</div>

 oBox.childNodes.length;  //Chrome數值9IE6784

 

為解決相容性問題(到底空文本算不算兒子,所以要封裝函數來解決):

可以利用nodeType是不是1來過濾文本節點、註釋節點等,編寫一個函數,得到一個標簽真正的子節點。

jQuery也有這層過濾:

//封裝一個children函數,這個函數能返回obj對象的所有真正兒子節點
function chidlren(obj,num){
    var arr = []; //存儲所有兒子
    //遍歷所有的節點
    for(var i = 0; i < obj.childNodes.length;i++){
        //遍歷的過程,尋找真正的HTML兒子節點、過濾文本、註釋等節點
        //判斷節點類型是不是1
        if(obj.childNodes[i].nodeType == 1){
            arr.push(obj.childNodes[i]); //如果是兒子節點就插入數組中
        }
    }
    //return arr;
    //返回的是:如果用戶傳入了num,返回某一個兒子,如果沒有num,返回所有兒子
    return num ? arr[num] : arr;
}
chidlren(oBox)[2].style.backgroundColor = 'red';
chidlren(oBox,3).style.backgroundColor = 'red';

2.2 parentNode父親節點

parentNode屬性表示父親節點。任何節點的parentNodenodeType一定是1,也就是說父親節點一定是標簽節點。文本節點、註釋節點沒有兒子。

 

var input = document.getElementsByTagName('input');
for(var i = 0;i < input.length;i++){
   //當點擊某個input時,如果自己被選中,此時改變父親的顏色為綠色,否則為白色
   input[i].onclick = function(){
       if(this.checked){
           this.parentNode.style.backgroundColor = 'green';
       }else{
           this.parentNode.style.backgroundColor = 'white';
       }
   }
}

2.3 previousSiblingnextSibling兄弟節點

上一個兄弟previousSibling、下一個兄弟nextSibling。同樣的,文本節點也屬於節點,註釋也是節點,所以一個節點的上一個兄弟可能是文本、註釋節點。原生JS中沒有提供類似nextAll()prevAll()siblings()方法,如果節點沒有上一個兄弟或下一個兄弟、返回null

 

 console.log(pp.previousSibling.nodeType)  

 console.log(pp.nextSibling.nodeType)

 

var pp = document.getElementById("pp");
//返回obj的前面一個兄弟
function prevSibling(obj){
    //開始遍歷obj節點的前面,直到遇見一個nodeType為1的節點
    var prev = obj;
    //迴圈遍歷。註意while的條件是一個賦值語句!賦值語句也有表達式的
    while(prev = prev.previousSibling){
        if(prev.nodeType == 1){
            return prev;
        }
    }
    return null;
}
//得到真正的後面兄弟
function nextSibling(obj){
    //開始遍歷obj節點的前面,直到遇見一個nodeType為1的節點
    var next = obj;
    while(next = next.nextSibling){
        if(next.nodeType == 1){
            return next;
        }
    }
    return null;
}
//返回obj的前面所有兄弟
function prevAll(obj){
    //開始遍歷obj節點的前面,直到遇見一個nodeType為1的節點
    var prev = obj;
    var arr = [];
    while(prev = prev.previousSibling){
        if(prev.nodeType == 1){
            arr.push(prev);
        }
    }
    return arr;
}
prevSibling(pp).style.background = "red";
nextSibling(pp).style.background = "green";
prevAll(pp)[1].style.background = "green";

三、原生JavaScript DOM節點操作

HTML節點我們原來最多就是改改HTML屬性,比如改改src屬性;或者改改css樣式,比如.style或者.css()

現在的問題是,我們要增加節點、刪除節點、移動節點、替換節點。

 


3.1 createElement()創建和appendChild()添加

創建節點的方法: create創建,Element元素。接收一個參數,就是創建的標簽是什麼。

 document.createElement()

 

追加節點的方法:創建出來的節點不在DOM樹上,所以就應該用appendChild()來添加到DOM樹上:

 父親.appendChild(新兒子);

 

var btn = document.getElementById('btn');
var txt = document.getElementById('txt');
var ul = document.getElementsByTagName('ul')[0];
btn.onclick = function(){
   //創建一個li標簽,用變數oLi 來表示,創建除了的節點不是任何節點的兒子(沒有在DOM樹上)
   var oLi = document.createElement('li');
   oLi.innerHTML = txt.value; //改變這個節點的內容
   //把新創建的節點,追加到DOM樹上
   ul.appendChild(oLi);
}

appendChild()一般來說就是用來追加新創建的節點,如果試圖把頁面上已經有的節點,appendChild()到別的地方,那麼這個節點將移動,也就是說,同一個節點不可能在頁面上兩個地方出現。

比如:

<div id="box1">
    <p id="xiaoming">我是小明</p>
</div>
<div id="box2">

</div>
<script type="text/javascript">
     var box2 = document.getElementById('box2');
     var xiaoming = document.getElementById('xiaoming');
     box2.appendChild(xiaoming);
</script>

以上將把xioaming移動到box2裡面。

 

innerHTML創建節點:

事實上,工作的時候很少用createElement。因為innerHTML足夠好用,innerHTML也可以用來創建節點,甚至效率createElement還高。

var year = document.getElementById('year');
for(var i = 1950; i <= 2018;i++){
   //創建節點
   var op = document.createElement('option');
   //改變創建出來的節點內容
   op.innerHTML = i;
   //上DOM樹
   year.appendChild(op); // 父親.appendChild(新兒子);
}

 

innerHTML創建:

for(var i = 1950; i <= 2018; i++) {
    year.innerHTML += '<option>'+i+'</option>';
}

 

JavaScript是動態變數:

var oBox = document.getElementById('box');
//得到box裡面所有的p,現在box沒有p,所以ops是一個空數組
var ops = oBox.getElementsByTagName('p');
var np = document.createElement('p'); //創建節點
oBox.appendChild(np); //追加節點
var np = document.createElement('p'); //創建節點
oBox.appendChild(np); //追加節點
var np = document.createElement('p'); //創建節點
oBox.appendChild(np); //追加節點
var np = document.createElement('p'); //創建節點
oBox.appendChild(np); //追加節點
var np = document.createElement('p'); //創建節點
oBox.appendChild(np); //追加節點
//這裡彈出多少?初學者認為彈出0,因為先得到數組p,然後創建節點,節點又沒有往數組裡面push,所以應該ops數不變才對。
//但是JS中存儲DOM節點的變數是動態,不是一個瞬時照片,而是一個有生命的動態對象,當oBox裡面的p標簽變化時,ps也變化
console.log(ops.length)

3.2 insertBefore()添加

appendChild是把新節點插入在父親的所有子節點的後面,也就是說添加的節點就是父親的最後一個兒子。

我們可以在任意一個位置添加子節點:會在原有標桿兒子之前插入

 父親.insertBefore(新兒子,原有標桿兒子)

 

var btn = document.getElementById('btn');
var txt = document.getElementById('txt');
var ul = document.getElementsByTagName('ul')[0];
var lis = document.getElementsByTagName('li');
btn.onclick = function(){
   //創建一個li標簽,用變數oLi 來表示,創建除了的節點不是任何節點的兒子(沒有在DOM樹上)
   var oLi = document.createElement('li');
   oLi.innerHTML = txt.value; //改變這個節點的內容
   //把新創建的節點,追加到DOM樹上
   //在lis[0]的前面插入
   ul.insertBefore(oLi,lis[0]);
}

如果想每次都在開頭添加,那麼就是:

 ul.insertBefore(oLi,lis[0]);

lis這個變數是動態的,這次添加的li,下回就是lis[0]


3.3 removeChild()刪除

父親.removeChild(兒子);
<ul>
    <li>吃飯 <a href="###">刪除</a></li>
    <li>睡覺 <a href="###">刪除</a></li>
    <li>打豆豆 <a href="###">刪除</a></li>
</ul>
<script type="text/javascript">
     var ul = document.getElementsByTagName('ul')[0];
     var lis = document.getElementsByTagName('li');
     var as = document.getElementsByTagName('a');

     for(var i = 0;i< as.length;i++){
        as[i].onclick = function(){
            ul.removeChild(this.parentNode);

            //如果要自殺,也要找到爸爸
            //this.parentNode.removeChild(this);
        }
     }
</script>

 

如果要自殺,也要找到爸爸:

 this.parentNode.removeChild(this);

 


3.4 replaceChild()替換

替換節點:

 父親.replaceChild(新兒子,舊兒子)

 

<div>
    <p>趙麗穎</p>
    <p id="xh">小黑</p>
    <p>迪麗熱巴</p>
</div>
<script type="text/javascript">
     var oBox = document.getElementsByTagName('div')[0]
     var xh = document.getElementById('xh');

     //創建節點,孤兒節點
     var op = document.createElement('p');
     op.innerHTML = '朱老師'; //更改op的內容
     oBox.replaceChild(op,xh);
</script>

3.5 clone()克隆

克隆節點,參數true表示深度克隆,節點裡面的所有內容和事件一同複製。

複製之後的節點是個孤兒節點,所以也要使用appendChild()等方法來添加上DOM樹。

 

 克隆對象.cloneNode(true)

 

<div id="box1">
    <ul>
        <li><span>趙麗穎</span></li>
        <li><span>迪麗熱巴</span></li>
        <li><span>柳岩</span></li>
        <li><span>志玲姐姐</span></li>
    </ul>
</div>
<div id="box2">

</div>
<script type="text/javascript">
     var box1 = document.getElementById('box1');
     var box2 = document.getElementById('box2');
     var ul = document.getElementsByTagName('ul')[0];
     var lis  = document.getElementsByTagName('li');

     //克隆li和li的所有後代(要加true),然後追加到ul中
     //ul.appendChild(lis[0].cloneNode());  //克隆第0個li
     box2.appendChild(ul.cloneNode(true)); //克隆ul追加到box2中
</script>

四、事件監聽

一堆理論知識正要來襲。

4.1事件流

我們考慮一個結構,三個div嵌套,點擊最內層的div,我們點擊了誰?僅僅點擊了最內層div嗎?不是就像手指放在到一個同心圓中,實際上手指觸碰到了任何一個圓。

點擊最內層的div,實際上瀏覽器會認為我們點擊了所有的盒子,甚至於bodydocumentwindow

 

為了描述事件的傳播,人為規定了一個事件的傳播方向,稱為“事件流”。兩個階段:事件捕獲階段,事件冒泡階段。

“事件流”描述的是頁面上各個元素接收事件的順序。

 

 


4.2 DOM0級事件監聽

DOM分級別,DOM0級、1級、2級、3級,是不同的標準,標準一直在升級。

之前學習的on開頭的語法添加事件,稱為“DOM0級事件”。

 

事件的觸發一定是按照事件流的順序,由於DOM0級只能監聽冒泡階段,所以順序是:box3box2box1bodydocumentwindow 如果改變監聽順序,彈出順序不變。

box1.onclick = function(){ alert('我是box1');}
box2.onclick = function(){ alert('我是box2');}
box3.onclick = function(){ alert('我是box3');}
document.body.onclick = function(){alert('我是body')}
document.onclick = function(){alert('我是document')}
window.onclick = function(){alert('我是window')}

這種監聽寫法,就是DOM0級,就是把onclick當做屬性添加給了div元素。

這種事件添加方法,只能監聽冒泡過程,不能監聽事件捕獲階段。

 

DOM0級事件處理函數中,this指的是觸發事件的DOM元素,就是事件傳播到的這個元素。

DOM0級事件處理函數中,如果同一個對象,同一個事件名,綁定多個監聽,後面寫的覆蓋前面寫的。

 box1.onclick = function(){ alert('我是box1');}

 box1.onclick = function(){ alert('我是box1,後面寫的');}

DOM0級事件,IE67事件只能冒泡到documentIE8只能冒泡body,不能繼續冒泡到window。也就是說不能給window對象添加事件。

 


4.3 DOM2級事件監聽

DOM1級規範中,沒有對事件進行改動,所以沒有DOM1級的事情

DOM2級做了新的規範,不用on**來綁定監聽了,而是用一個方法

W3C推出了addEventListener()函數,add添加、event事件,listener監聽

它接收三個參數:事件,函數,是否監聽捕獲階段

 元素.addEventListener(事件,事件處理函數,是否添加到捕獲階段)

第一個參數:事件名不用謝on。(clickmouseover

第二個參數:函數可以是匿名函數,也可以是有名函數

第三個參數:布爾值,true表示監聽

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前面兩個終於把webpack相關配置解析完了。 現在終於進入vue的開發了 vue組件化開發預熱 前期準備 ...
  • React Native最近有大動作,於2019年3月12日發佈新版本0.59。主要有兩點值得升級:支持React Hooks;升級了JavaScriptCore,使Android性能有大幅提升。據用過的人說,長列表快速滑動而產生的白屏得到大大改善。這麼好的東西得趕緊體驗一下啊。 簡單的執行npm ...
  • 好久沒寫博客,從一道題目開始吧 實現一個sum函數,支持sum(1,2)和sum(1)(2)兩種調用方式 說實在的,沒啥難的,很簡單寫出來 或者用ES6的展開操作符,或者是arguments 考察的內容其實就是函數作為返回值,簡單涉及到了閉包,還有arguments的判斷,ES6展開操作符的剩餘語法 ...
  • async await的執行 註意: 本次代碼僅在 Chrome 73 下進行測試。 start 不瞭解 async await 的,先去看阮一峰老師的文章 "async 函數" 。 先來看一道 "頭條的面試題" ,這其實是考察瀏覽器的 event loop. 運行結果如下: Event Loop ...
  • webpack是一個現代JavaScript應用程式的靜態模塊打包器,借用官網的一張圖,它能夠將一些預處理語言,js的最新語法轉換成瀏覽器識別的內容。現在一般的前端框架都有比較成熟的腳手架,大多數對webpack都有個較好的集成,我們只需要敲一些簡單的命令就能生成一個通用的項目模板,比較便捷,但是要 ...
  • 數組扁平化 數組扁平化即將多維數組轉化為一維數組: 例: [1,2,3,4,5,[2,3,4,[6,10]]]==>[1,2,3,4,5,2,3,4,6,10]; 實現數組扁平化的方法有好幾種,在這裡只說一種我比較常用的,自己感覺比較核心的方法,即遍曆數組arr,若arr[i]為數組則遞歸遍歷,直至 ...
  • JavaScript歷史大概在1992年,一家稱作Nombas的公司開始開發一種叫做C–(C-minus-minus,簡稱Cmm)的嵌入式腳本語言。 Cmm背後的理念很簡單:一個足夠強大可以替代巨集操作(macro)的腳本語言,同時保持與C(和C++)中夠的相似性,以便開發人員能很快學會。 這個腳本語 ...
  • 1、首先,我們要準備HTML代碼: 這裡的<a>標簽裡面的href=“#top”就表示點擊它就可以回到頂部,就不寫回到頂部的代碼了 2、設置其CSS樣式: 一頓胡亂操作之後,“返回頂部”按鈕就有瞭如下這個外觀:,還怪好看的(不是 3、重點來了,jQuery代碼部分: 解釋一下:首先我們要做的功能是: ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...