1. 目標
- 能夠說出什麼是DOM
- 能夠獲取頁面元素
- 能夠給元素註冊事件
- 能夠操作DOM元素的屬性
- 能夠創建元素
- 能夠操作DOM節點
2. DOM簡介
2.1 什麼是DOM呢?
文檔對象模型( Document Object Model ,簡稱DOM),是W3C推薦的處理可擴展標記語言( HTML或者XML )的標準編程接口。
W3C已經定義了一系列的DOM接口,通過這些DOM接口可以改變網頁的內容,結構和樣式。
2. DOM樹
html 上面的標籤時 document。document代表整個文檔
- 文檔:一個頁面就是一個文檔,DOM中使用 document 表示
- 元素:頁面中的所有標籤都是元素,DOM中使用 element 表示
- 節點:網頁中的所有內容都是節點(標籤,屬性,文本,註釋等),DOM中使用node表示
DOM 把以上的內容都看作是對象
3. 獲取元素
在實際開發中主要用來操作元素。
3.1 獲取頁面元素
3.1.1 根據 ID 獲取
- 返回一個匹配特點 ID 的元素
- id是大小寫敏感的字符串
栗子:
<body>
<div id="time">2020-4-8</div>
<script>
// 1. 因爲文檔頁面從上往下加載,所以得先有標籤,所以我們script寫到標籤的下面
// 2. 獲得 元素 通過 id(翻譯)駝峯命名法
// 3. 參數 id 是大小寫敏感的字符串,所以加引號。
// 4. 返回的是一個元素對象
div = document.getElementById("time");
console.log(time);
console.log(typeof(time));
// 5. console.dir 打印我們返回的元素對象 更好的查看裏面的屬性與方法
console.dir(time);
</script>
</body>
3.1.2 根據標籤名獲取
使用 getElementByTagName() 方法可以返回帶有指定標籤名的對象集合。
栗子:
<body>
<ul>
<li>帥哥是我 我是帥哥1</li>
<li>帥哥是我 我是帥哥2</li>
<li>帥哥是我 我是帥哥3</li>
<li>帥哥是我 我是帥哥4</li>
<li>帥哥是我 我是帥哥5</li>
</ul>
<ul id="nav">
<li>噓!~ 禁止想象 1</li>
<li>噓!~ 禁止想象 2</li>
<li>噓!~ 禁止想象 3</li>
<li>噓!~ 禁止想象 4</li>
<li>噓!~ 禁止想象 5</li>
</ul>
<script>
// 返回的是 獲取過來元素對象的集合 以僞數組的形式存儲
var list = document.getElementsByTagName('li');
console.dir(list);
console.log(list[0]);
// 我們想要依次打印裏面的元素對象我們可以採用遍歷的方式
for (var i = 0; i < list.length; i++) {
console.log(list[i]);
}
// 3. element.getElementsByTagName() 可以得到這個元素裏面的某個標籤
var nav = document.getElementById('nav'); //可以獲得 nav 元素
var navList = nav.getElementsByTagName('li');
console.dir(navList);
</script>
</body>
3. 通過 HTML5 新增的方法獲取
- getElementsByClassName(‘類名’)
- querySelctor( )
- querySelctorAll( )
栗子:
<body>
<div class="box">盒子1</div>
<div class="box">盒子2</div>
<div id="nav">
<ul>
<li>首頁</li>
<li>產品</li>
</ul>
</div>
<script>
// 1. getElementsByClassName('類名') 根據類名返回元素對象集合
var box = document.getElementsByClassName('box');
console.log(box);
// 2. querySelctor 返回選擇器的第一個元素對象
var firstBox = document.querySelector('.box');
console.log(firstBox);
var nav = document.querySelector('#nav');
console.log(nav);
var li = document.querySelector('li');
console.log(li);
// 3. querySelctorAll() 返回指定選擇器的所有對象集合
var allBox = document.querySelectorAll('.box');
console.log(allBox);
var lis = document.querySelectorAll('li');
console.log((lis));
</script>
</body>
4. 特殊元素獲取
<body>
<script>
/* 獲取body */
var bodyEle = document.body;
console.log(bodyEle);
/* 獲取html */
var htmlEle = document.documentElement;
console.log(htmlEle);
</script>
</body>
4. 事件基礎
JavaScript使我們有能力創建動態頁面事件是可以被JavaScript偵測到的行爲。
簡單理解:觸發—響應機制。
網頁中的每個元素都可以產生某些可以觸發 JavaScript的事件,例如,我們可以在用戶點擊某按鈕是產生一個事件,然後去執行某些操作。
栗子:唐伯虎點秋香
<body>
<button id="btn">唐伯虎</button>
<script>
// 點擊一個按鈕,彈出對話框
// 1. 事件是有三部分組成 事件源 事件類型 事件處理程序 我們也稱爲事件三要素
// (1) 事件源 事件被觸發的對象 誰 按鈕
var btn = document.getElementById('btn');
// (2) 事件類型 如何觸發 什麼事件 比如鼠標點擊(onclick) 還是鼠標經過 還是鍵盤按下
// (3) 事件處理程序 通過一個函數賦值的方式 完成
btn.onclick = function() {
alert('點秋香');
}
</script>
</body>
執行事件的步驟
- 獲取事件源
- 註冊事件(綁定事件)
- 添加事件處理程序(採取函數賦值的形式)
常見鼠標事件
鼠標事件 | 觸發條件 |
---|---|
onclick | 鼠標點擊左鍵觸發 |
onmouseover | 鼠標經過觸發 |
onmouseout | 鼠標離開觸發 |
onmousemove | 鼠標移動觸發 |
onmouseenter | 鼠標經過觸發 |
onmouseleave | 鼠標離開觸發 |
onfocus | 獲得鼠標焦點觸發 |
onblur | 失去鼠標焦點觸發 |
onmouseup | 鼠標彈起觸發 |
onmousedown | 鼠標按下觸發 |
栗子:
<body>
<div>123</div>
<script>
//執行事件步驟
//點擊 div 控制檯輸出 我被選中了
// 1. 獲取事件源
var div = document.querySelector('div');
// 2. 綁定事件 註冊事件
// div.onclick
// 3. 添加事件處理程序
div.onclick = function() {
console.log('我被選中了');
}
</script>
</body>
5. 操作元素
JavaScript 的 DOM 操作可以改變網頁內容,結構和樣式,我們可以利用 DOM 操作元素來改變元素裏面的內容,屬性等,注意以下都是屬性
5.1 改變元素內容
element.innerText
從起始位置到終止位置的內容,但它除去html標籤,同時空格和換行也會同時去掉
element.innerHtml
-
栗子二:
二者區別:
<body>
<div></div>
<p>
我是文字
<span>123</span>
</p>
<script>
// innerText 和 innerHTML 的區別
// 1. innerText 不識別 html標籤 非標準
// var div = document.querySelector('div');
// div.innerText = "<strong>最帥</strong>是我";
// 2. innerHTML 識別 html 標籤 W3C 標準
// div.innerHTML = "<strong>最帥</strong>是我";
// 這兩個屬性是可讀寫的 可以獲取元素裏面的內容
// var p = document.querySelector('p');
// console.log(p.innerText);
// console.log(p.innerHTML);
</script>
</body>
起始位置到終止位置的全部內容,包括html標籤,同時保留空格和換行
innerHTML標準,儘量用
5.2 改變元素屬性
再來個栗子:
通過不同時間得到不同的問候語
<body>
<img src="" alt="">
<div>上午好</div>
<script>
// 1. 獲取元素
var img = document.querySelector('img');
var div = document.querySelector('div');
// 2. 獲取當前小時數
var date = new Date();
var h = date.getHours();
console.log(h);
// 3. 判斷小時數
switch (true) {
case h < 12:
img.src = "morning.jpg"
div.innerHTML = '親!早上好';
break;
case h > 12 && h < 17:
img.src = "afternoon.jpg"
div.innerHTML = '親!下午好';
break;
default:
img.src = "night.jpg"
div.innerHTML = '親!晚上好';
break;
}
</script>
</body>
5.3 表單元素的屬性操作
type, value, checked, selected, disabled(禁用按鈕)
表單裏面的值是通過value來變化的
5.4 樣式屬性操作
我們可以通過 js 修改元素的大小,顏色,位置等樣式
方法 | 作用 |
---|---|
element.style | 行內樣式操作如果說樣式比較少 或者 功能簡單的情況下使用 |
element.className | 類名樣式操作 如果說樣式比較多或者功能比較複雜那麼就用這個(更好的分離樣式與行爲) |
注:
- js裏面的樣式採用駝峯命名法 比如 fontSize, backgroundColor
- js 修改 style 樣式操作,產生的是行內樣式, 權重高。
-
栗子1( element.style ):隱藏文本框的內容
當鼠標點擊文本框的時候,裏面的默認文字隱藏,當鼠標離開文本框時,裏面的文字顯示。 -
栗子2( element.className ):改變class名
- 如果樣式修改較多,可以採取操作類名方式更改元素樣式
- class是個保留字,因此使用className來操作元素類名屬性
- className會直接更改元素的類名,會覆蓋原先的類名 所以要加新的類名要把原先的寫上
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
div {
width: 200px;
height: 200px;
color: saddlebrown;
background-color: red;
}
.change {
margin: 20px;
color: silver;
background-color: royalblue;
}
</style>
</head>
<body>
<div>我是帥哥</div>
<script>
var div = document.getElementsByTagName('div');
div[0].onclick = function() {
this.className = 'change';
}
</script>
</body>
</html>
5.5 排他思想
如果有同一組元素我們想要改變某一個元素實現某種樣式,需要用到循環的排他思想:
- 所有元素清空樣式(幹掉其他人)
- 給當前元素設置樣式(留下我自己)
- 注意順序不能顛倒,首先幹掉其他人,在設置自己
-
- 給一組元素註冊事件
- 點擊後頁面背景變化
5.5 自定義屬性的操作
獲取方法 | 不同點 |
---|---|
element.屬性 | 只能獲得內置的屬性(元素本身自帶的) |
element.getAttribute(‘屬性’) | 主要獲得自定義的屬性(標準) |
5.5 設置屬性值
設置方法 | 不同點 |
---|---|
element.屬性 = ‘屬性值’ | 內置的屬性 |
element.setAttribute(‘屬性’, ‘屬性值’) | 自定義屬性 |
注:class比較特殊element.setAttribute('class', '啥啥啥')
記住不是className。
5.6 移除屬性
element.removeAttribute('屬性');
5.7 H5自定義屬性
一:
- 自定義屬性的目的:是爲了保存並使用數據,有些數據可以保存到頁面中而不用保存到數據庫
- 自定義屬性是通過getAttribute(‘屬性’)獲取。
- 但是有些自定義屬性很容易引起歧義,不容易判斷是元素的內置屬性還是自定義屬性
注: 爲了解決上述問題我們在設置自定義屬性的時候以 data- 開頭作爲屬性名並賦值
例:<div data-index = "1"><div>
二:
-
js 添加自定義屬性及屬性值:
element.setAttribute('屬性', '啥啥啥') = ‘啥啥啥’
-
js 獲取自定義屬性值:
element.getAttribute('屬性')
(兼容性獲取方法)element.dataset.自定義屬性名
(H5新增方法)element.dataset['自定義屬性名']
(H5新增方法)- 注:如果自定義屬性中有多個 "- " 鏈接的單詞, 我們獲取的時候採取的是 駝峯命名法
栗子(important):
<body>
<div getTime='20' data-index='2' data-list-name="andy"> </div>
<script>
var div = document.querySelector('div');
console.log(div.getAttribute('getTime')); /* 傳統獲取方法 */
div.setAttribute('data-time', 20); /* 傳統添加方式 */
console.log(div.getAttribute('data-index')); /* H5新增命名方法 */
console.log(div.getAttribute('data-list-name'));
/* h5新增的獲取自定義屬性的方法, 只能獲取 data- 開頭的自定義屬性 ie 11纔開始識別*/
/* dataset是一個集合裏面存放了所有以data開頭的自定義屬性 */
console.log(div.dataset); /* 所有的自定義屬性都存放在裏面 */
console.log(div.dataset.index); /* 和對象的用法一樣,直接提取即可 */
console.log(div.dataset['index']); /* 第二種方法提取 */
/* 如果自定義屬性中有多個 " - " 鏈接的單詞, 我們獲取的時候採取的是 駝峯命名法*/
console.log(div.dataset.listName);
console.log(div.dataset['listName']);
</script>
</body>
6. 節點操作
6.1 爲啥學習節點操作
利用DOM提供的方法獲取元素 | 利用節點的層級關係獲取元素 |
---|---|
document.getElementById() | 1. 利用利用 父 子 兄 節點關係獲取元素 |
document.getElementByTagName() | |
document.querySelector等 | |
邏輯性不強,繁瑣 | 邏輯性強,操作簡單,但是兼容性差, |
6.2 節點概述
一般的,節點至少擁有nodeType(節點類型),nodeName(節點名稱)和nodeValue(節點值)這三個基本屬性。
- 元素節點 nodeType 爲1
- 屬性節點 nodeType 爲2
- 文本節點 nodeType 爲3(文本節點包含文字,空格,換行等)
- 注:我們在實際開發中,節點操作的是元素節點
6.3 節點層級
利用 DOM 樹可以把節點劃分爲不同的層級關係,常見的是 父 子 兄 的層級關係間 DOM 樹
6.3.1 父節點獲取(element.parentNode)
栗子(important):
<body>
<div class="box">
<span class="erweima"></span>
</div>
<script>
/* 1. 父節點 parentNode */
var erweima = document.querySelector('.erweima');
/* 2. 得到的是離元素最近的父節點,如果找不到就返回空 */
console.log(erweima.parentNode);
</script>
</body>
6.3.2 子節點獲取(element.childNodes)
- parentNode.childNodes (標準)
parentNode.childNodes返回包含指定節點的子元素的集合,該集合爲即時更新的集合
注意:
- 返回值裏面包含了所有的子節點,包括元素節點,文本節點等
- 如果只想要獲得裏面的元素節點,則需要專門處理,所以我們一般不提倡使用childNodes
- 如果非要用那麼要加一個條件判斷(利用 元素節點 nodeType 爲1這一性質)
<body>
<ul>
<li>我很帥</li>
<li>我很帥</li>
<li>我很帥</li>
<li>我很帥</li>
</ul>
<script>
var ul = document.querySelector('ul');
for (var i = 0; i < ul.childNodes.length; i++) {
if (ul.childNodes[i].nodeType == 1) {
// ul.childNodes[i] 是元素節點
console.log(ul.childNodes[i]);
}
}
</script>
</body>
- parentNode.children (非標準) (庶出)但是卻被廣泛的應用(各個瀏覽器都支持)
<body>
<ul>
<li>我很帥</li>
<li>我很帥</li>
<li>我很帥</li>
<li>我很帥</li>
</ul>
<script>
/* DOM提供的方法 (API) 獲取 */
var ul = document.querySelector('ul');
var lis = ul.querySelectorAll('li');
/* 1. 子節點 childNodes 所有的子節點 包含 元素節點 文本節點等等 */
console.log(ul.childNodes);
console.log(ul.childNodes[0].nodeType);
console.log(ul.childNodes[1].nodeType);
/* 2. children 獲取所有的子元素節點 也是實際開發中常用的 */
console.log(ul.children);
</script>
</body>
6.3.3 第一個和最後一個子節點的獲取
- element.firstChild :第一個子節點 不管是文本節點還是元素節點
- element.lastChild:同理
- element.firstElementChild :返回第一個子元素節點,找不到返回null
- element.lastElementChild : 同理
注:3 4 種方法有兼容性問題,IE9 以上才支持
實際開發的寫法 既沒有兼容性問題又返回第一個子元素 : console.log(ul.children[0]);
實際開發的寫法 既沒有兼容性問題又返回最後一個子元素 : console.log(ul.children[ul.children.length -1]);
栗子:
<body>
<ul>
<li>我很帥1</li>
<li>我很帥2</li>
<li>我很帥3</li>
<li>我很帥4</li>
</ul>
<script>
var ul = document.querySelector('ul');
/* firstChild 第一個子節點 不管是文本節點還是元素節點 */
console.log(ul.firstChild);
/* 同上 */
console.log(ul.lastChild);
/* firstElementChild 返回第一個子元素節點,找不到返回null*/
console.log(ul.firstElementChild);
/* lastElementChild 返回最後一個子元素節點,找不到返回null*/
console.log(ul.lastElementChild);
/* 實際開發的寫法 既沒有兼容性問題又返回第一個子元素 */
console.log(ul.children[0]);
/* 實際開發的寫法 既沒有兼容性問題又返回最後一個子元素 */
console.log(ul.children[ul.children.length - 1]);
</script>
</body>
6.3.4兄弟節點
- node.nextSibling : 返回下一個兄弟節點,找不到返回null,包含所有的節點
- node.previousSibling : 返回上一個兄弟節點,找不到返回null,包含所有的節點
- node.nextElementSibling : 返回當前元素下一個兄弟節點(元素節點),找不到則返回null
- node.previousElementSibling : 返回當前元素上一個兄弟節點(元素節點),找不到則返回null
注:3 4 種方法有兼容性問題,IE9 以上才支持
栗子:
<body>
<div>我是div</div>
<span>我是span</span>
<script>
var div = document.querySelector('div');
/* 得到了文本節點,下一個兄弟節點 包含元素節點或者文本節點等等*/
console.log(div.nextSibling);
/* 得到了文本節點,上一個兄弟節點 包含元素節點或者文本節點等等*/
console.log(div.previousSibling);
console.log(div.nextElementSibling);
console.log(div.previousElementSibling);
</script>
</body>
6.4 創建節點,添加節點
- document.createElement(‘element’) : 創建一個元素
- 父級.appendChild(變量); : 添加在後面
- 父級.insertBefore(變量, 父級.children[0]) : 傳入變量,和要插入的位置
<body>
<ul>
<li>123</li>
</ul>
<script>
/* 1. 創建節點元素 */
var li = document.createElement('li');
/* 2. 添加節點 node.appendChild(child) node 父級 child 子級 後面追加元素 類似於數組中的push */
var ul = document.querySelector('ul');
ul.appendChild(li);
/* 3. 添加節點 node.insertBefore(child, 指定元素); */
var lili = document.createElement('li');
ul.insertBefore(lili, ul.children[0]);
/* 4. 我們想要頁面添加一個新的元素: 1. 創建元素 2. 添加元素 */
</script>
</body>
栗子
6.5 刪除節點
node.removeChild(child):DOM 中刪除一個子節點,刪除一個子節點,返回刪除的節點
<body>
<button>delate</button>
<ul>
<li>熊大</li>
<li>熊二</li>
<li>光頭強</li>
</ul>
<script>
// 1. 獲取元素
var ul = document.querySelector('ul');
var btn = document.querySelector('button');
// 2. 刪除元素
// 3. 點擊依次刪除
btn.onclick = function() {
// if(ul.children.length == 0){
// this.disabled = true;
// }
for (var i = 0; i < ul.children.length; i++) {
ul.removeChild(ul.children[0]);
}
}
</script>
</body>
阻止鏈接跳轉添加javascript:void(0);
或者javascript:;
栗子
6.6 複製節點(克隆節點)
node.cloneNode():返回調用該方法的節點的一個副本,也稱爲克隆節點,拷貝節點
注意:
- 如果括號參數爲空或者爲false,則是淺拷貝,即只克隆複製節點本身,不克隆裏面的子節點
<body>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
<script>
var ul = document.querySelector('ul');
var lili = ul.children[0].cloneNode(true);
ul.insertBefore(lili, ul.children[0]);
</script>
</body>