文檔對象模型(Document Object Model,簡稱 DOM),是 W3C 組織推薦的處理可擴展標記語言(HTML或者XML)的標準編程接口,W3C 已經定義了一系列的 DOM 接口,通過這些 DOM 接口可以改變網頁的內容、結構和樣式。
DOM 把以上內容都看做是對象
DOM在我們實際開發中主要用來操作元素。
我們如何來獲取頁面中的元素呢?
獲取頁面中的元素可以使用以下幾種方式:
使用 getElementByld() 方法可以獲取帶ID的元素對象
doucument.getElementByld('id名')
使用 console.dir() 可以打印我們獲取的元素對象,更好的查看對象里面的屬性和方法。
示例
<div id="time">2019-9-9</div>
<script>
// 1.因為我們文檔頁面從上往下加載,所以得先有標簽,所以script寫在標簽下面
// 2.get 獲得 element 元素 by 通過 駝峰命名法
// 3.參數 id是大小寫敏感的字符串
// 4.返回的是一個元素對象
var timer = document.getElementById('time');
console.log(timer);
// 5. console.dir 打印我們的元素對象,更好的查看里面的屬性和方法
console.dir(timer);
</script>
根據標簽名獲取,使用 getElementByTagName() 方法可以返回帶有指定標簽名的對象的集合
doucument.getElementsByTagName('標簽名');
<ul>
<li>知否知否,應是等你好久</li>
<li>知否知否,應是等你好久</li>
<li>知否知否,應是等你好久</li>
<li>知否知否,應是等你好久</li>
<li>知否知否,應是等你好久</li>
</ul>
<script>
// 1.返回的是獲取過來元素對象的集合 以偽數組的形式存儲
var lis = document.getElementsByTagName('li');
console.log(lis);
console.log(lis[0]);
// 2.依次打印,遍歷
for (var i = 0; i < lis.length; i++) {
console.log(lis[i]);
}
// 3.如果頁面中只有 1 個 li,返回的還是偽數組的形式
// 4.如果頁面中沒有這個元素,返回的是空偽數組
</script>
還可以根據標簽名獲取某個元素(父元素)內部所有指定標簽名的子元素,獲取的時候不包括父元素自己
element.getElementsByTagName('標簽名')
ol.getElementsByTagName('li');
注意:父元素必須是單個對象(必須指明是哪一個元素對象),獲取的時候不包括父元素自己
<script>
//element.getElementsByTagName('標簽名'); 父元素必須是指定的單個元素
var ol = document.getElementById('ol');
console.log(ol.getElementsByTagName('li'));
</script>
根據類名返回元素對象合集
document.getElementsByClassName('類名');
根據指定選擇器返回第一個元素對象
document.querySelector('選擇器');
// 切記里面的選擇器需要加符號
// 類選擇器.box
// id選擇器 #nav
var firstBox = document.querySelector('.box');
根據指定選擇器返回所有元素對象
document.querySelectorAll('選擇器');
注意:
querySelector 和 querySelectorAll 里面的選擇器需要加符號,比如: document.querySelector('#nav');
<script>
// 1. getElementsByClassName 根據類名獲得某些元素集合
var boxs = document.getElementsByClassName('box');
console.log(boxs);
// 2. querySelector 返回指定選擇器的第一個元素對象 切記 里面的選擇器需要加符號 .box #nav
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. querySelectorAll()返回指定選擇器的所有元素對象集合
var allBox = document.querySelectorAll('.box');
console.log(allBox);
var lis = document.querySelectorAll('li');
console.log(lis);
</script>
返回body元素對象
document.body;
返回html元素對象
document.documentElement;
JavaScript 使我們有能力創建動態頁面,而事件是可以被 JavaScript 偵測到的行為。
簡單理解: 觸發— 響應機制。
網頁中的每個元素都可以產生某些可以觸發 JavaScript 的事件,例如,我們可以在用戶點擊某按鈕時產生一個事件,然后去執行某些操作。
<script>
// 點擊一個按鈕,彈出對話框
// 1. 事件是有三部分組成 事件源 事件類型 事件處理程序 我們也稱為事件三要素
//(1) 事件源 事件被觸發的對象 誰 按鈕
var btn = document.getElementById('btn');
//(2) 事件類型 如何觸發 什么事件 比如鼠標點擊(onclick) 還是鼠標經過 還是鍵盤按下
//(3) 事件處理程序 通過一個函數賦值的方式 完成
btn.onclick = function() {
alert('點秋香');
}
</script>
<script>
// 執行事件步驟
// 點擊div 控制臺輸出 我被選中了
// 1. 獲取事件源
var div = document.querySelector('div');
// 2.綁定事件 注冊事件
// div.onclick
// 3.添加事件處理程序
div.onclick = function() {
console.log('我被選中了');
}
</script>
鼠標事件 | 觸發條件 |
onclick | 鼠標點擊左鍵觸發 |
onmouseover | 鼠標經過觸發 |
onmouseout | 鼠標離開觸發 |
onfocus | 獲得鼠標焦點觸發 |
onblur | 失去鼠標焦點觸發 |
onmousemove | 鼠標移動觸發 |
onmouseup | 鼠標彈起觸發 |
onmousedown | 鼠標按下觸發 |
JavaScript 的 DOM 操作可以改變網頁內容、結構和樣式,我們可以利用 DOM 操作元素來改變元素里面的內容 、屬性等。注意以下都是屬性
從起始位置到終止位置的內容,但它去除html標簽,同時空格和換行也會去掉。
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> 2019';
// 2. innerHTML 識別html標簽 保留空格和換行的
div.innerHTML = '<strong>今天是:</strong> 2019';
// 這兩個屬性是可讀寫的 可以獲取元素里面的內容
var p = document.querySelector('p');
console.log(p.innerText);
console.log(p.innerHTML);
</script>
</body>
// img.屬性
img.src = "xxx";
input.value = "xxx";
input.type = "xxx";
input.checked = "xxx";
input.selected = true / false;
input.disabled = true / false;
我們可以通過 JS 修改元素的大小、顏色、位置等樣式。
// element.style
div.style.backgroundColor = 'pink';
div.style.width = '250px';
// element.className
注意:
<body>
<div class="first">文本</div>
<script>
// 1. 使用 element.style 獲得修改元素樣式 如果樣式比較少 或者 功能簡單的情況下使用
var test = document.querySelector('div');
test.onclick = function() {
// this.style.backgroundColor = 'purple';
// this.style.color = '#fff';
// this.style.fontSize = '25px';
// this.style.marginTop = '100px';
// 讓我們當前元素的類名改為了 change
// 2. 我們可以通過 修改元素的className更改元素的樣式 適合于樣式較多或者功能復雜的情況
// 3. 如果想要保留原先的類名,我們可以這么做 多類名選擇器
// this.className = 'change';
this.className = 'first change';
}
</script>
</body>
如果有同一組元素,我們相要某一個元素實現某種樣式,需要用到循環的排他思想算法:
<body>
<button>按鈕1</button>
<button>按鈕2</button>
<button>按鈕3</button>
<button>按鈕4</button>
<button>按鈕5</button>
<script>
// 1. 獲取所有按鈕元素
var btns = document.getElementsByTagName('button');
// btns得到的是偽數組 里面的每一個元素 btns[i]
for (var i = 0; i < btns.length; i++) {
btns[i].onclick = function() {
// (1) 我們先把所有的按鈕背景顏色去掉 干掉所有人
for (var i = 0; i < btns.length; i++) {
btns[i].style.backgroundColor = '';
}
// (2) 然后才讓當前的元素背景顏色為pink 留下我自己
this.style.backgroundColor = 'pink';
}
}
//2. 首先先排除其他人,然后才設置自己的樣式 這種排除其他人的思想我們成為排他思想
</script>
</body>
element.屬性;
element.getAttribute('屬性');
element.屬性 = '值';
element.setAttribute('屬性','值');
element.removeAttribute('屬性');
<body>
<div id="demo" index="1" class="nav"></div>
<script>
var div = document.querySelector('div');
// 1. 獲取元素的屬性值
// (1) element.屬性
console.log(div.id);
//(2) element.getAttribute('屬性') get得到獲取 attribute 屬性的意思 我們程序員自己添加的屬性我們稱為自定義屬性 index
console.log(div.getAttribute('id'));
console.log(div.getAttribute('index'));
// 2. 設置元素屬性值
// (1) element.屬性= '值'
div.id = 'test';
div.className = 'navs';
// (2) element.setAttribute('屬性', '值'); 主要針對于自定義屬性
div.setAttribute('index', 2);
div.setAttribute('class', 'footer'); // class 特殊 這里面寫的就是class 不是className
// 3 移除屬性 removeAttribute(屬性)
div.removeAttribute('index');
</script>
</body>
自定義屬性目的:
H5規定自定義屬性 data-開頭作為屬性名并賦值
<div data-index = "1"></>
// 或者使用JavaScript設置
div.setAttribute('data-index',1);
<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'));
console.log(div.getAttribute('data-list-name'));
// h5新增的獲取自定義屬性的方法 它只能獲取data-開頭的
// 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>
獲取元素通常使用兩種方式:
1.利用DOM提供的方法獲取元素 | 2.利用節點層級關系獲取元素 |
document.getElementById() | 利用父子兄節點關系獲取元素 |
document.getElementsByTagName() | 邏輯性強,但是兼容性較差 |
document.querySelector 等 | |
邏輯性不強,繁瑣 |
這兩種方式都可以獲取元素節點,我們后面都會使用,但是節點操作更簡單
一般的,節點至少擁有三個基本屬性
網頁中的所有內容都是節點(標簽、屬性、文本、注釋等),在DOM 中,節點使用 node 來表示。
HTML DOM 樹中的所有節點均可通過 JavaScript 進行訪問,所有 HTML 元素(節點)均可被修改,也可以創建或刪除。
一般的,節點至少擁有nodeType(節點類型)、nodeName(節點名稱)和nodeValue(節點值)這三個基本屬性。
我們在實際開發中,節點操作主要操作的是元素節點
利用 DOM 樹可以把節點劃分為不同的層級關系,常見的是父子兄層級關系。
node.parentNode
<body>
<!-- 節點的優點 -->
<div>我是div</div>
<span>我是span</span>
<ul>
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
</ul>
<div class="demo">
<div class="box">
<span class="erweima">×</span>
</div>
</div>
<script>
// 1. 父節點 parentNode
var erweima = document.querySelector('.erweima');
// var box = document.querySelector('.box');
// 得到的是離元素最近的父級節點(親爸爸) 如果找不到父節點就返回為 null
console.log(erweima.parentNode);
</script>
</body>
parentNode.childNodes(標準)
parentNode.children(非標準)
<body>
<ul>
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
</ul>
<ol>
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
<li>我是li</li>
</ol>
<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>
parentNode.firstChild
parentNode.lastChild
<body>
<ol>
<li>我是li1</li>
<li>我是li2</li>
<li>我是li3</li>
<li>我是li4</li>
<li>我是li5</li>
</ol>
<script>
var ol = document.querySelector('ol');
// 1. firstChild 第一個子節點 不管是文本節點還是元素節點
console.log(ol.firstChild);
console.log(ol.lastChild);
// 2. firstElementChild 返回第一個子元素節點 ie9才支持
console.log(ol.firstElementChild);
console.log(ol.lastElementChild);
// 3. 實際開發的寫法 既沒有兼容性問題又返回第一個子元素
console.log(ol.children[0]); //第一個子元素節點
console.log(ol.children[ol.children.length - 1]);//最后一個子元素節點
</script>
</body>
parentNode.firstElementChild
parentNode.lastElementChild
實際開發中,firstChild 和 lastChild 包含其他節點,操作不方便,而 firstElementChild 和 lastElementChild 又有兼容性問題,那么我們如何獲取第一個子元素節點或最后一個子元素節點呢?
解決方案
// 數組元素個數減1 就是最后一個元素的索引號
parentNode.chilren[parentNode.chilren.length - 1]
<body>
<ol>
<li>我是li1</li>
<li>我是li2</li>
<li>我是li3</li>
<li>我是li4</li>
</ol>
<script>
var ol = document.querySelector('ol');
// 1.firstChild 獲取第一個子結點的,包含文本結點和元素結點
console.log(ol.firstChild);
// 返回的是文本結點 #text(第一個換行結點)
console.log(ol.lastChild);
// 返回的是文本結點 #text(最后一個換行結點)
// 2. firstElementChild 返回第一個子元素結點
console.log(ol.firstElementChild);
// <li>我是li1</li>
// 第2個方法有兼容性問題,需要IE9以上才支持
// 3.實際開發中,既沒有兼容性問題,又返回第一個子元素
console.log(ol.children[0]);
// <li>我是li1</li>
console.log(ol.children[3]);
// <li>我是li4</li>
// 當里面li個數不唯一時候,需要取到最后一個結點時這么寫
console.log(ol.children[ol.children.length - 1]);
</script>
</body>
node.nextSibling
node.previousSibling
node.nextElementSibling
node.previousElementSibling
示例
<body>
<div>我是div</div>
<span>我是span</span>
<script>
var div = document.querySelector('div');
// 1.nextSibling 下一個兄弟節點 包含元素節點或者 文本節點等等
console.log(div.nextSibling); // #text
console.log(div.previousSibling); // #text
// 2. nextElementSibling 得到下一個兄弟元素節點
console.log(div.nextElementSibling); //<span>我是span</span>
console.log(div.previousElementSibling);//null
</script>
</body>
如何解決兼容性問題 ?
答:自己封裝一個兼容性的函數
function getNextElementSibling(element) {
var el = element;
while(el = el.nextSibling) {
if(el.nodeType === 1){
return el;
}
}
return null;
}
document.createElement('tagName');
node.appendChild(child)
node.insertBefore(child,指定元素)
示例
<body>
<ul>
<li>123</li>
</ul>
<script>
// 1. 創建節點元素節點
var li = document.createElement('li');
// 2. 添加節點 node.appendChild(child) node 父級 child 是子級 后面追加元素 類似于數組中的push
// 先獲取父親ul
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>
node.removeChild(child)
node.cloneNode()
示例
<body>
<ul>
<li>1111</li>
<li>2</li>
<li>3</li>
</ul>
<script>
var ul = document.querySelector('ul');
// 1. node.cloneNode(); 括號為空或者里面是false 淺拷貝 只復制標簽不復制里面的內容
// 2. node.cloneNode(true); 括號為true 深拷貝 復制標簽復制里面的內容
var lili = ul.children[0].cloneNode(true);
ul.appendChild(lili);
</script>
</body>
三種動態創建元素的區別
區別:
<body>
<div class="innner"></div>
<div class="create"></div>
<script>
// 2. innerHTML 創建元素
var inner = document.querySelector('.inner');
// 2.1 innerHTML 用拼接字符串方法
for (var i = 0; i <= 100; i++) {
inner.innerHTML += '<a href="#">百度</a>';
}
// 2.2 innerHTML 用數組形式拼接
var arr = [];
for (var i = 0; i <= 100; i++) {
arr.push('<a href="#">百度</a>');
}
inner.innerHTML = arr.join('');
// 3.document.createElement() 創建元素
var create = document.querySelector('.create');
var a = document.createElement('a');
create.appendChild(a);
</script>
</body>
總結:不同瀏覽器下, innerHTML 效率要比 createElement 高
對于DOM操作,我們主要針對子元素的操作,主要有
給元素添加事件,稱為注冊事件或者綁定事件。
注冊事件有兩種方式:傳統方式和方法監聽注冊方式
傳統注冊方式 | 方法監聽注冊方式 |
利用 on 開頭的事件 onclick | w3c 標準推薦方式 |
<button onclick = "alert("hi")"></button> | addEventListener() 它是一個方法 |
btn.onclick = function() {} | IE9 之前的 IE 不支持此方法,可使用 attachEvent() 代替 |
特點:注冊事件的唯一性 | 特點:同一個元素同一個事件可以注冊多個監聽器 |
同一個元素同一個事件只能設置一個處理函數,最后注冊的處理函數將會覆蓋前面注冊的處理函數 | 按注冊順序依次執行 |
eventTarget.addEventListener(type,listener[,useCapture])
該方法接收三個參數:
<body>
<button>傳統注冊事件</button>
<button>方法監聽注冊事件</button>
<button>ie9 attachEvent</button>
<script>
var btns = document.querySelectorAll('button');
// 1. 傳統方式注冊事件
btns[0].onclick = function() {
alert('hi');
}
btns[0].onclick = function() {
alert('hao a u');
}
// 2. 事件監聽注冊事件 addEventListener
// (1) 里面的事件類型是字符串 所以加引號 而且不帶on
// (2) 同一個元素 同一個事件可以添加多個偵聽器(事件處理程序)
btns[1].addEventListener('click', function() {
alert(22);
})
btns[1].addEventListener('click', function() {
alert(33);
})
// 3. attachEvent ie9以前的版本支持
btns[2].attachEvent('onclick', function() {
alert(11);
})
</script>
</body>
eventTarget.attachEvent(eventNameWithOn,callback)
該方法接收兩個參數:
兼容性處理的原則:首先照顧大多數瀏覽器,再處理特殊瀏覽器
function addEventListener(element, eventName, fn) {
// 判斷當前瀏覽器是否支持 addEventListener 方法
if (element.addEventListener) {
element.addEventListener(eventName, fn); // 第三個參數 默認是false
} else if (element.attachEvent) {
element.attachEvent('on' + eventName, fn);
} else {
// 相當于 element.onclick = fn;
element['on' + eventName] = fn;
}
eventTarget.removeEventListener(type,listener[,useCapture]);
該方法接收三個參數:
eventTarget.detachEvent(eventNameWithOn,callback);
該方法接收兩個參數:
eventTarget.onclick = null;
事件刪除示例:
<body>
<div>1</div>
<div>2</div>
<div>3</div>
<script>
var divs = document.querySelectorAll('div');
divs[0].onclick = function() {
alert(11);
// 1. 傳統方式刪除事件
divs[0].onclick = null;
}
// 2.removeEventListener 刪除事件
divs[1].addEventListener('click',fn); //里面的fn不需要調用加小括號
function fn(){
alert(22);
divs[1].removeEventListener('click',fn);
}
// 3.IE9 中的刪除事件方式
divs[2].attachEvent('onclick',fn1);
function fn1() {
alert(33);
divs[2].detachEvent('onclick',fn1);
}
</script>
</body>
function removeEventListener(element, eventName, fn) {
// 判斷當前瀏覽器是否支持 removeEventListener 方法
if (element.removeEventListener) {
element.removeEventListener(eventName, fn); // 第三個參數 默認是false
} else if (element.detachEvent) {
element.detachEvent('on' + eventName, fn);
} else {
element['on' + eventName] = null;
}
加深理解:
我們向水里面扔一塊石頭,首先它會有一個下降的過程,這個過程就可以理解為從最頂層向事件發生的最具體元素(目標點)的捕獲過程;之后會產生泡泡,會在最低點( 最具體元素)之后漂浮到水面上,這個過程相當于事件冒泡。
兩個盒子嵌套,一個父盒子一個子盒子,我們的需求是當點擊父盒子時彈出 father ,當點擊子盒子時彈出 son
<body>
<div class="father">
<div class="son">son盒子</div>
</div>
<script>
// dom 事件流 三個階段
// 1. JS 代碼中只能執行捕獲或者冒泡其中的一個階段。
// 2. onclick 和 attachEvent(ie) 只能得到冒泡階段。
// 3. 捕獲階段 如果addEventListener 第三個參數是 true 那么則處于捕獲階段 document -> html -> body -> father -> son
var son = document.querySelector('.son');
son.addEventListener('click', function() {
alert('son');
}, true);
var father = document.querySelector('.father');
father.addEventListener('click', function() {
alert('father');
}, true);
</script>
</body>
但是因為DOM流的影響,我們點擊子盒子,會先彈出 father,之后再彈出 son
這是因為捕獲階段由 DOM 最頂層節點開始,然后逐級向下傳播到到最具體的元素接收
<body>
<div class="father">
<div class="son">son盒子</div>
</div>
<script>
// 4. 冒泡階段 如果addEventListener 第三個參數是 false 或者 省略 那么則處于冒泡階段 son -> father ->body -> html -> document
var son = document.querySelector('.son');
son.addEventListener('click', function() {
alert('son');
}, false);
var father = document.querySelector('.father');
father.addEventListener('click', function() {
alert('father');
}, false);
document.addEventListener('click', function() {
alert('document');
})
</script>
</body>
我們點擊子盒子,會彈出 son、father、document
這是因為冒泡階段開始時由最具體的元素接收,然后逐級向上傳播到到 DOM 最頂層節點
eventTarget.onclick = function(event) {
// 這個 event 就是事件對象,我們還喜歡的寫成 e 或者 evt
}
eventTarget.addEventListener('click', function(event) {
// 這個 event 就是事件對象,我們還喜歡的寫成 e 或者 evt
})
簡單理解:
事件發生后,跟事件相關的一系列信息數據的集合都放到這個對象里面
這個對象就是事件對象 event,它有很多屬性和方法,比如“
誰綁定了這個事件
鼠標觸發事件的話,會得到鼠標的相關信息,如鼠標位置
鍵盤觸發事件的話,會得到鍵盤的相關信息,如按了哪個鍵
<body>
<div>123</div>
<script>
// 事件對象
var div = document.querySelector('div');
div.onclick = function(e) {
// console.log(e);
// console.log(window.event);
// e = e || window.event;
console.log(e);
}
// 1. event 就是一個事件對象 寫到我們偵聽函數的 小括號里面 當形參來看
// 2. 事件對象只有有了事件才會存在,它是系統給我們自動創建的,不需要我們傳遞參數
// 3. 事件對象 是 我們事件的一系列相關數據的集合 跟事件相關的 比如鼠標點擊里面就包含了鼠標的相關信息,鼠標坐標啊,如果是鍵盤事件里面就包含的鍵盤事件的信息 比如 判斷用戶按下了那個鍵
// 4. 這個事件對象我們可以自己命名 比如 event 、 evt、 e
// 5. 事件對象也有兼容性問題 ie678 通過 window.event 兼容性的寫法 e = e || window.event;
</script>
</body>
事件對象本身的獲取存在兼容問題:
解決:
e = e || window.event;
事件對象屬性方法 | 說明 |
e.target | 返回觸發事件的對象 標準 |
e.srcElement | 返回觸發事件的對象 非標準 ie6-8使用 |
e.type | 返回事件的類型 比如click mouseover 不帶on |
e.cancelBubble | 該屬性阻止冒泡,非標準,ie6-8使用 |
e.returnValue | 該屬性阻止默認行為 非標準,ie6-8使用 |
e.preventDefault() | 該方法阻止默認行為 標準 比如不讓鏈接跳轉 |
e.stopPropagation() | 阻止冒泡 標準 |
e.target 和 this 的區別:
<body>
<div>123</div>
<a href="http://www.baidu.com">百度</a>
<form action="http://www.baidu.com">
<input type="submit" value="提交" name="sub">
</form>
<script>
// 常見事件對象的屬性和方法
// 1. 返回事件類型
var div = document.querySelector('div');
div.addEventListener('click', fn);
div.addEventListener('mouseover', fn);
div.addEventListener('mouseout', fn);
function fn(e) {
console.log(e.type);
}
// 2. 阻止默認行為(事件) 讓鏈接不跳轉 或者讓提交按鈕不提交
var a = document.querySelector('a');
a.addEventListener('click', function(e) {
e.preventDefault(); // dom 標準寫法
})
// 3. 傳統的注冊方式
a.onclick = function(e) {
// 普通瀏覽器 e.preventDefault(); 方法
// e.preventDefault();
// 低版本瀏覽器 ie678 returnValue 屬性
// e.returnValue;
// 我們可以利用return false 也能阻止默認行為 沒有兼容性問題 特點: return 后面的代碼不執行了, 而且只限于傳統的注冊方式
return false;
alert(11);
}
</script>
</body>
事件冒泡:開始時由最具體的元素接收,然后逐級向上傳播到到 DOM 最頂層節點
事件冒泡本身的特性,會帶來的壞處,也會帶來的好處,需要我們靈活掌握。
e.stopPropagation();
e.cancelBubble = true;
<body>
<div class="father">
<div class="son">son兒子</div>
</div>
<script>
// 常見事件對象的屬性和方法
// 阻止冒泡 dom 推薦的標準 stopPropagation()
var son = document.querySelector('.son');
son.addEventListener('click', function(e) {
alert('son');
e.stopPropagation(); // stop 停止 Propagation 傳播
e.cancelBubble = true; // 非標準 cancel 取消 bubble 泡泡
}, false);
var father = document.querySelector('.father');
father.addEventListener('click', function() {
alert('father');
}, false);
document.addEventListener('click', function() {
alert('document');
})
</script>
</body>
if(e && e.stopPropagation){
e.stopPropagation();
}else{
window.event.cancelBubble = true;
}
e.target 與 this 的區別
<body>
<div>123</div>
<ul>
<li>abc</li>
<li>abc</li>
<li>abc</li>
</ul>
<script>
// 常見事件對象的屬性和方法
// 1. e.target 返回的是觸發事件的對象(元素) this 返回的是綁定事件的對象(元素)
// 區別 : e.target 點擊了那個元素,就返回那個元素 this 那個元素綁定了這個點擊事件,那么就返回誰
var div = document.querySelector('div');
div.addEventListener('click', function(e) {
console.log(e.target);
console.log(this);
})
var ul = document.querySelector('ul');
ul.addEventListener('click', function(e) {
// 我們給ul 綁定了事件 那么this 就指向ul
console.log(this);
console.log(e.currentTarget);
// e.target 指向我們點擊的那個對象 誰觸發了這個事件 我們點擊的是li e.target 指向的就是li
console.log(e.target);
})
// 了解兼容性
// div.onclick = function(e) {
// e = e || window.event;
// var target = e.target || e.srcElement;
// console.log(target);
// }
// 2. 了解 跟 this 有個非常相似的屬性 currentTarget ie678不認識
</script>
</body>
事件對象本身的獲取存在兼容問題:
解決方案
<body>
<div>123</div>
<script>
// 事件對象
var div = document.querySelector('div');
div.onclick = function(e) {
// e = e || window.event;
console.log(e);
// 事件對象也有兼容性問題 ie678 通過 window.event 兼容性的寫法 e = e || window.event;
}
</body>
<body>
<ul>
<li>知否知否,點我應有彈框在手!</li>
<li>知否知否,點我應有彈框在手!</li>
<li>知否知否,點我應有彈框在手!</li>
<li>知否知否,點我應有彈框在手!</li>
<li>知否知否,點我應有彈框在手!</li>
</ul>
<script>
// 事件委托的核心原理:給父節點添加偵聽器, 利用事件冒泡影響每一個子節點
var ul = document.querySelector('ul');
ul.addEventListener('click', function(e) {
// alert('知否知否,點我應有彈框在手!');
// e.target 這個可以得到我們點擊的對象
e.target.style.backgroundColor = 'pink';
// 點了誰,就讓誰的style里面的backgroundColor顏色變為pink
})
</script>
</body>
以上案例:給 ul 注冊點擊事件,然后利用事件對象的 target 來找到當前點擊的 li,因為點擊 li,事件會冒泡到 ul 上, ul 有注冊事件,就會觸發事件監聽器。
鼠標事件 | 觸發條件 |
onclick | 鼠標點擊左鍵觸發 |
onmouseover | 鼠標經過觸發 |
onmouseout | 鼠標離開觸發 |
onfocus | 獲得鼠標焦點觸發 |
onblur | 失去鼠標焦點觸發 |
onmousemove | 鼠標移動觸發 |
onmouseup | 鼠標彈起觸發 |
onmousedown | 鼠標按下觸發 |
<body>
<h1>我是一段不愿意分享的文字</h1>
<script>
// 1. contextmenu 我們可以禁用右鍵菜單
document.addEventListener('contextmenu', function(e) {
e.preventDefault(); // 阻止默認行為
})
// 2. 禁止選中文字 selectstart
document.addEventListener('selectstart', function(e) {
e.preventDefault();
})
</script>
</body>
鼠標事件對象 | 說明 |
e.clientX | 返回鼠標相對于瀏覽器窗口可視區的X坐標 |
e.clientY | 返回鼠標相對于瀏覽器窗口可視區的Y坐標 |
e.pageX(重點) | 返回鼠標相對于文檔頁面的X坐標 IE9+ 支持 |
e.pageY(重點) | 返回鼠標相對于文檔頁面的Y坐標 IE9+ 支持 |
e.screenX | 返回鼠標相對于電腦屏幕的X坐標 |
e.screenY | 返回鼠標相對于電腦屏幕的Y坐標 |
示例:
<body>
<script>
// 鼠標事件對象 MouseEvent
document.addEventListener('click', function(e) {
// 1. client 鼠標在可視區的x和y坐標
console.log(e.clientX);
console.log(e.clientY);
console.log('---------------------');
// 2. page 鼠標在頁面文檔的x和y坐標
console.log(e.pageX);
console.log(e.pageY);
console.log('---------------------');
// 3. screen 鼠標在電腦屏幕的x和y坐標
console.log(e.screenX);
console.log(e.screenY);
})
</script>
</body>
鍵盤事件 | 觸發條件 |
onkeyup | 某個鍵盤按鍵被松開時觸發 |
onkeydown | 某個鍵盤按鍵被按下時觸發 |
onkeypress | 某個鍵盤按鍵被按下時觸發,但是它不識別功能鍵,比如 ctrl shift 箭頭等 |
<body>
<script>
// 常用的鍵盤事件
//1. keyup 按鍵彈起的時候觸發
// document.onkeyup = function() {
// console.log('我彈起了');
// }
document.addEventListener('keyup', function() {
console.log('我彈起了');
})
//3. keypress 按鍵按下的時候觸發 不能識別功能鍵 比如 ctrl shift 左右箭頭啊
document.addEventListener('keypress', function() {
console.log('我按下了press');
})
//2. keydown 按鍵按下的時候觸發 能識別功能鍵 比如 ctrl shift 左右箭頭啊
document.addEventListener('keydown', function() {
console.log('我按下了down');
})
// 4. 三個事件的執行順序 keydown -- keypress -- keyup
</script>
</body>
鍵盤事件對象 屬性 | 說明 |
keyCode | 返回該鍵值的ASCII值 |
言
Hi,大家好,我是希留。
上篇文章帶大家認識了Vue,以及初始化了一個Vue工程的項目。今天繼續介紹一下Vue的一些基礎用法。
目錄
使用開發工具(idea、webstorm、vscode…)打開Vue項目。由于后端用idea的比較多,這里以idea為例(需要安裝Vue.js插件),打開后目錄結構如下:
如上圖所示,Vue的目錄結構主要由以下部分組成:
my-vue-project
├--- build
├--- config
├--- node_modules
└--- src
├--- assets
└--- components
├--- router
└--- App.vue
├--- main.js
├--- static
└--- package.json
build :構建相關,用來存放項目構建腳本,對項目進行編譯打包
config :配置相關,用來存放基本的配置文件
node_modules :用來存放的是項目的所有依賴
src :存放項目的源碼
assets :主題 字體等靜態資源
components :全局公用組件
router :路由
App.vue :入口頁面
main.js :入口 加載組件 初始化等
static :第三方不打包資源
package.json :定義了項目的所有依賴,包括開發時依賴和發布時依賴
你看到的 v-bind 特性被稱為指令。指令帶有前綴 v-
除了使用插值表達式{{}}進行數據渲染,也可以使用 v-bind指令,它的簡寫的形式就是一個冒號(:)
代碼如下:
<template>
<div class="hello">
<!-- 如果要將模型數據綁定在html屬性中,則使用 v-bind 指令
此時title中顯示的是模型數據-->
<h1 v-bind:title="message">
{{content}}
</h1>
<!-- v-bind 指令的簡寫形式:冒號(:) -->
<h1 :title="message">
{{content}}
</h1>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
data () {
return {
content: '我是標題',
message: '頁面加載于 ' + new Date().toLocaleString()
}
}
}
</script>
運行項目,瀏覽器打開地址,可看到頁面正常渲染出來數據。
雙向數據綁定和單向數據綁定;
使用 v-model 進行雙向數據綁定:
<template>
<div class="hello">
...
!-- v-bind:value只能進行單向的數據渲染 -->
<input type="text" v-bind:value="searchMap.keyWord">
<!-- v-model 可以進行雙向的數據綁定 -->
<input type="text" v-model="searchMap.keyWord">
<p>您要查詢的是:{{searchMap.keyWord}}</p>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
data () {
return {
...
searchMap: {
keyWord: '希留'
}
}
}
}
</script>
使用v-bind綁定的數據是單向的,并不會隨著輸入值的改變而改變
使用 v-model 指令 才能把數據雙向綁定
修飾符 (Modifiers) 是以半角句號(.)指明的特殊后綴,用于指出一個指令應該以特殊方式綁定。
例如,.prevent 修飾符告訴 v-on 指令對于觸發的事件調用 event.preventDefault()方法
(即阻止事件原本的默認行為)
示例代碼如下:
<template>
<div class="hello">
...
<!-- 修飾符用于指出一個指令應該以特殊方式綁定。
這里的 .prevent 修飾符告訴 v-on 指令對于觸發的事件調用js的 event.preventDefault()方法
(該方法表示阻止表單提交的默認行為) -->
<form action="save" v-on:submit.prevent="onSubmit">
<label for="username">
<input type="text" id="username" v-model="user.username">
<button type="submit">保存</button>
</label>
</form>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
data () {
return {
...
user: {}
}
},
methods: {
onSubmit() {
if (this.user.username) {
console.log('提交表單')
} else {
alert('請輸入用戶名')
}
}
}
}
</script>
當在方法上加了.preven修飾符后,點擊保存只會執行onSubmit方法。若沒加,則會先執行onSubmit方法在執行action里的方法。
v- 前綴作為一種視覺提示,用來識別模板中 Vue 特定的 attribute。Vue 為 v-bind 和 v-on 這兩個最常用的指令,提供了特定簡寫,它們看起來可能與普通的 HTML 略有不同,但 : 與 @ 對于 attribute 名來說都是合法字符,在所有支持 Vue 的瀏覽器都能被正確地解析。而且,它們不會出現在最終渲染的標記中。
v-bind 縮寫
<!-- 完整語法 -->
<a v-bind:href="url">...</a>
<!-- 縮寫 -->
<a :href="url">...</a>
<!-- 動態參數的縮寫 (2.6.0+) -->
<a :[key]="url"> ... </a>
v-on 縮寫
<!-- 完整語法 -->
<a v-on:click="doSomething">...</a>
<!-- 縮寫 -->
<a @click="doSomething">...</a>
<!-- 動態參數的縮寫 (2.6.0+) -->
<a @[event]="doSomething"> ... </a>
模板內的表達式非常便利,但是設計它們的初衷是用于簡單運算的。在模板中放入太多的邏輯會讓模板過重且難以維護。
所以Vue提供了計算屬性。對于任何復雜邏輯,你都應當使用計算屬性。且計算屬性是基于它們的響應式依賴進行緩存的。只在相關響應式依賴發生改變時它們才會重新求值。
示例代碼如下:
<template>
<div class="hello">
...
<p>原始值: "{{ message }}"</p>
<!-- 1、插值數據綁定中使用表達式 -->
<p>反轉消息: {{ message.split('').reverse().join('') }}</p>
<!-- 2、使用計算屬性 -->
<p>反轉消息: "{{ reversedMessage }}"</p>
</div>
</template>
<script>
export default {
name: 'HelloWorld',
data () {
return {
...
message: '上海自來水來自海上 haha'
}
},
computed: {
reversedMessage () {
return this.message.split('').reverse().join('')
}
}
}
</script>
雖然計算屬性在大多數情況下更合適,但有時也需要一個自定義的偵聽器。這就是為什么 Vue 通過 watch 選項提供了一個更通用的方法,來響應數據的變化。當需要在數據變化時執行異步或開銷較大的操作時,這個方式是最有用的。
示例代碼如下:
<template>
<div class="hello">
...
<p>
Ask a yes/no question:
<input v-model="question">
<button @click="getAnswer()">提問</button>
</p>
<p>{{ answer }}</p>
</div>
</template>
<script>
import axios from 'axios'
export default {
name: 'HelloWorld',
data () {
return {
...
message: '上海自來水來自海上 haha'
}
},
methods: {
getAnswer() {
var vm = this
axios.get('https://yesno.wtf/api')
.then(function (response) {
vm.answer = _.capitalize(response.data.answer)
})
.catch(function (error) {
vm.answer = '網找不到API接口. ' + error
})
}
},
watch: {
// 如果 `question` 發生改變,這個函數就會運行
question: function (newQuestion, oldQuestion) {
this.answer = '等待您停止輸入...'
this.getAnswer()
}
}
}
</script>
在這個示例中,使用 watch 選項允許我們執行異步操作 (訪問一個 API),限制我們執行該操作的頻率,并在我們得到最終結果前,設置中間狀態。這些都是計算屬性無法做到的。
v-if 指令用于條件性地渲染一塊內容。這塊內容只會在指令的表達式返回 truthy 值的時候被渲染。
v-if 是一個指令,所以必須將它添加到一個元素上。v-else-if,充當 v-if 的“else-if 塊”,可以連續使用;
v-else 指令來表示 v-if 的“else 塊”,必須緊跟在帶 v-if 或者 v-else-if 的元素的后面,否則它將不會被識別。
另一個用于根據條件展示元素的選項是 v-show 指令。用法大致一樣,不同的是帶有 v-show 的元素始終會被渲染并保留在 DOM 中。v-show 只是簡單地切換元素的 CSS property display。v-show 不支持 元素,也不支持 v-else。
兩者對比:
我們可以用 v-for 指令基于一個數組來渲染一個列表。v-for 指令需要使用 item in items 形式的特殊語法,其中 items 是源數據數組,而 item 則是被迭代的數組元素的別名。
為了給 Vue 一個提示,以便它能跟蹤每個節點的身份,從而重用和重新排序現有元素,你需要為每項提供一個唯一 key attribute,建議盡可能在使用 v-for 時提供 key attribute,因為它是 Vue 識別節點的一個通用機制,key 并不僅與 v-for 特別關聯。
示例代碼如下:
<!-- 1、遍歷數據列表 -->
<table border="1">
<tr v-for="(item, index) in userList" :key="item.id">
<td>{{index}}</td>
<td>{{item.id}}</td>
<td>{{item.username}}</td>
<td>{{item.age}}</td>
</tr>
</table>
<script>
export default {
name: 'HelloWorld',
data () {
return {
...
userList: [
{ id: 1, username: 'helen', age: 18 },
{ id: 2, username: 'peter', age: 28 },
{ id: 3, username: 'andy', age: 38 }
]
}
},
}
</script>
過濾器常用來處理文本格式化的操作。過濾器可以用在兩個地方:雙花括號插值和 v-bind 表達式
過濾器應該被添加在 JavaScript 表達式的尾部,由“管道”符號指示
```javascript
<!-- 1、遍歷數據列表 -->
<table border="1">
<tr v-for="(item, index) in userList" :key="item.id">
<td>{{index}}</td>
<td>{{item.id}}</td>
<td>{{item.username}}</td>
<td>{{item.age}}</td>
<!-- | 管道符號:表示使用后面的過濾器處理前面的數據 -->
<td>{{item.gender | genderFilter}}</td>
</tr>
</table>
<script>
export default {
name: 'HelloWorld',
data () {
return {
...
userList: [
{ id: 1, username: 'helen', age: 18 ,gender: 1},
{ id: 2, username: 'peter', age: 28 ,gender: 1},
{ id: 3, username: 'andy', age: 38 ,gender: 0}
]
}
},
// filters 定義局部過濾器,只可以在當前vue實例中使用
filters: {
genderFilter(gender) {
return gender === 1 ? '男' : '女'
}
}
}
</script>
結語
好了,以上就是今天要講的內容,本文簡單介紹了Vue的基礎使用,相信大家對Vue有了進一步的認識了,趕快動手操作起來吧,gogogo。
內容是《Web前端開發之Javascript視頻》的課件,請配合大師哥《Javascript》視頻課程學習。
DOM(Document Object Model,文檔對象模型):
DOM是一個使程序和腳本能夠動態地訪問和更新文檔的內容、結構以及樣式,并獨立于平臺和語言的接口;其定義了訪問和處理文檔的標準方法;是針對HTML和XML文檔的一個API;
DOM描繪了一個層次化的節點樹,允許開發者添加、移除和修改頁面的某一部分,從而重構整個文檔。
DOM脫胎于Netscape及微軟創始的DHTML,但現在已經成為表現和操作頁面標記的真正的跨平臺、語言中立的方式;
DOM與具體的編程語言無關,可以在C、Javascript、ActionScript、Java等語言中實現;
DOM標準的目標是讓“任何一種程序設計語言”能操控使用“任何一種標記語言”編寫出的“任何一份文檔”;
DOM 級別:
DOM Level 1:由兩個模塊組成:DOM核心(DOM Core)和DOM HTML;專注于 HTML 和 XML 文檔模型,它含有文檔導航和處理功能;
DOM Level 2:對 DOM1級做了擴展,添加了樣式表對象模型,并定義了操作附于文檔之上的樣式信息的功能性;同時還定義了一個事件模型,并提供了對 XML 命名空間的支持;
DOM Level 3:對DOM2級做了擴展,規定了內容模型 (DTD 和 Schemas) 和文檔驗證,同時規定了文檔加載和保存、文檔查看、文檔格式化和關鍵事件;
DOM Level 0:確切來說,不存在DOM0級,因為它不是 W3C 規范,而僅僅是對在 Netscape Navigator 3.0 和 Microsoft Internet Explorer 3.0 中的等價功能性的一種定義,實際上指的就是DHTML;
1998年10月 DOM1級規范成為W3C的標準,為基本的文檔結構及查詢提供了接口;
目前主流的瀏覽器都已實現了DOM1、基本實現了DOM2和3;
DOM組成:
XML:
XML 指可擴展標記語言(EXtensible Markup Language),它是一種標記語言,類似于HTML,它的設計宗旨是傳輸數據,而非顯示數據;
XML 標簽沒有被預定義,需要開發者自定義標簽;
XML 被設計為具有自我描述性,是W3C的推薦標準;
XML 的用途:把數據從 HTML 分離、簡化數據共享、簡化數據傳輸、簡化平臺的變更;
DOM樹:
DOM可以將任何HTML或XML文檔描繪成一個由多層節點構成的結構;
如HTML:
<html>
<head>
<title>零點網絡</title>
</head>
<body>
<h1>零點程序員</h1>
<p>zeronetwork <a href="#">王唯</a></p>
</body>
</html>
樹狀圖:
樹狀圖
每一個標簽是是文檔的一個節點,它表示一個Node對象;
節點分為幾種不同的類型,每種類型分別表示文檔中不同的信息或標記;
每種節點都擁有各自的特點、數據和方法,并且也與其他節點存在某種關系;
節點之間的關系構成了層次,而所有頁面標記則表現為一個以特定節點為根節點的樹形結構;
文檔節點(Document):是每個文檔的根節點;文檔節點只有一個子節點,即<html>元素,也稱為文檔元素,它是文檔最外層元素,其他所有元素都包含在文檔元素中;每個文檔只能有一個文檔元素;在XML中,沒有預定義的文檔元素,因此任何元素都可能成為文檔元素;
Node接口:
每一段標記都可以通過樹中的一個節點來表示,這個節點稱為Node;
DOM1級定義一個Node接口,其用于抽象地表示文檔中一個獨立的部分;
HTML元素通過元素節點表示,特性(attribute)通過特性節點表示,文檔類型通過文檔類型節點表示,注釋通過注釋節點表示,共12種節點類型,這些類型都繼承自一個基類型Node類型,因此所有節點類型都共享著相同的基本屬性和方法;
Node類型圖:
Node類型圖
Document和Element類型與HTMLDocument和HTMLElement類型之間是有嚴格的區分的;Document類型代表一個HTML或XML文檔,而HTMLDocment只是代表一個HTML文檔,XMLDocument代表是XML文檔;Element類型代表該文檔中的一個元素,而HTMLElement只是HTML文檔中的元素,不是XMLDocument中的元素;
HTMLElement的很多子類型代表HTML元素的具體類型,每個類型具有多個Javascript屬性,這些屬性對應具體的元素或元素組的HTML元素特性;這些具體的元素類也定義了額外的屬性和方法,它們并不是簡單的映射HTML元素及HTML元素特性;
每個節點都有一個nodeType屬性,用于表明節點的類型;
節點類型由在Node類型中定義的下列12個常數值來表示;
<div id="mydiv">零點程序員</div>
<script>
var mydiv = document.getElementById("mydiv");
console.log(mydiv.nodeType); // 1
console.log(Node.ELEMENT_NODE); // 1
if(mydiv.nodeType == Node.ELEMENT_NODE)
alert("mydiv is an element");
// IE8以下不支持,可以如此判斷
if(mydiv.nodeType == 1)
alert("IE8: mydiv is an element");
</script>
Node屬性:
nodeName : String,節點的名字,取決于節點的類型;
nodeValue : String,節點的值,取決于節點的類型;
nodeType : Number,節點的類型常數值之一;
注:對于元素節點,nodeName保存的始終是元素的標簽名,而nodeValue的值為null;因此在使用nodeName及nodeValue時,最好先檢測一下節點的類型;
var mydiv = document.getElementById("mydiv");
console.log(mydiv.nodeType); // 1
console.log(mydiv.nodeName); // DIV
console.log(mydiv.nodeValue); // null
if(mydiv.nodeType == Node.ELEMENT_NODE){
console.log(mydiv.nodeType); // 1
console.log(mydiv.nodeName); // DIV
}
var txt = mydiv.firstChild;
if(txt.nodeType == Node.TEXT_NODE){
console.log("nodeType:" + txt.nodeType); // 3
console.log("nodeName:" + txt.nodeName); // #text
console.log("nodeValue:" + txt.nodeValue); // 零點程序員
}
文檔中所有節點之間都存在著各種關系,理清這些關系是非常重要的;
childNodes屬性:
返回NodeList類型的所有子節點集合;
NodeList是一種類數組的對象,用于保存一組有序的節點,可以通過方括號來訪問保存在其中的節點,其擁有length 屬性;
<div id="mydiv">
<h2>零點程序員</h2>
<h3>zeronetwork</h3>
<div>從事IT教育,開展<a href="#">Web前端</a>、后端開發教育</div>
</div>
<script>
var mydiv = document.getElementById("mydiv");
// dom.html:16 NodeList(7) [text, h2, text, h3, text, div, text]
console.log(mydiv.childNodes);
console.log(mydiv.childNodes.length); // 7
var firstChild = mydiv.childNodes[1]; // 如果是0,即#text節點,此處是個空格
console.log(firstChild); // <h2>零點程序員</h2>
console.log(firstChild.nodeName); // H2
var secondChild = mydiv.childNodes.item(3);
console.log(secondChild); // <h3>zeronetwork</h3>
</script>
注:mydiv有三個子節點,但length卻是7個,多出4個text節點,此text節點,實際上是代碼中的換行或空格,如果把html代碼刪除換行或空格,length結果就是3;
NodeList對象的特點:是基于DOM結構動態執行查詢的結果,因此DOM結構的變化能夠自動反映在NodeList對象中;
var mydiv = document.getElementById("mydiv");
console.log(mydiv.childNodes.length); // 7
var p = document.createElement("p");
p.innerText = "大師哥王唯";
mydiv.appendChild(p);
console.log(mydiv.childNodes.length); // 8
在實際應用中,有可能需要將NodeList對象轉換成數組:
var mydiv = document.getElementById("mydiv");
var arrNodes = Array.prototype.slice.call(mydiv.childNodes,0);
console.log(arrNodes);
children屬性:
IE與其他瀏覽器對文本節點中空白符的解釋不一致,導致了children屬性出現;這個屬性是HTMLCollection的實例,其中只包含元素的子節點中那些也是元素的節點,即children列表中只包含Element元素;
children并不是標準屬性,但所有的瀏覽器都實現了該屬性;
var myList = document.getElementById("myList");
var lis = myList.children;
console.log(lis);
console.log(lis.length);
Text和Comment節點沒有children屬性;
HTMLCollection類型:
是一個接口,表示HTML 元素的集合,與NodeList非常類似,也是個類數組對象;元素在 HTMLCollection 對象中的順序與它們在文檔源代碼中出現的順序一樣;
HTMLCollection類型的屬性和方法:
item()方法:返回 HTMLCollection 中指定索引的元素;
length屬性:返回 HTMLCollection 中元素的數量;
namedItem()方法:返回 HTMLCollection 中指定 ID 或 name 屬性的元素;
console.log(lis.item(0));
console.log(lis[0]); // 一般用這個代替
console.log(lis.namedItem("myli"));
console.log(lis["myli"]); // 一般用這個代替
與NodeList類型一樣,HTMLCollection對象也是實時動態的;
parentNode屬性:
指向節點的父節點;
一個元素節點的父節點可能是一個元素(Element )節點,也可能是一個文檔(Document )節點,或者是個文檔碎片(DocumentFragment)節點;
對于Document、DocumentFragment和Attr對象來說,其parentNode屬性為null,因為它們沒有父節點;
另外,如果當前節點剛剛被建立,還沒有被插入到DOM樹中,則該節點的parentNode屬性也返回null;
var elt = document.getElementById("elt");
if(elt.parentNode){
elt.parentNode.removeChild(elt);
}
parentElement屬性:
返回當前節點的父元素節點,如果該元素沒有父節點,或者父節點不是一個 DOM 元素,則返回 null;parentElement是一個DOM元素對象(HTMLElement對象);
if(elt.parentElement)
elt.parentElement.style.backgroundColor = "purple";
在早期,parentElement是ie專用的,而現在所有的瀏覽器都已經實現了,并且被納入了最新的DOM4規范中;
parentElement匹配的是parent為Element的情況,而parentNode匹配的則是parent為Node的情況;Element是包含在Node里的,它的nodeType是1;一般情況parentNode可以取代parentElement的所有功能;
最能體現兩者的區別是以下兩行代碼:
console.log(document.documentElement); // <html>
console.log(document.documentElement.parentNode); // #document
console.log(document.documentElement.parentElement); // null
previousSibling: 前一個兄弟節點;如果這個節點就是第一個兄弟節點,那么該值為null;
nextSibling : 后一個兄弟節點:如果這個節點就是最后一個兄弟節點,那么該值為null;
var mydiv = document.getElementById("mydiv");
console.log(mydiv.parentNode); // <body>
var firstChild = mydiv.childNodes[0]; // 把0換成1或2
console.log(firstChild.previousSibling);
console.log(firstChild.nextSibling);
if(firstChild.nextSibling === null)
console.log("child is last node");
else
console.log("child is't last node");
// 遍歷
var elt = document.getElementById("elt");
while(elt){
console.log(elt.nodeName);
elt = elt.nextSibling;
}
firstChild : 指向在childNodes集合中的第一個節點;
lastChild : 指向在childNodes集合中最后一個節點;
即firstChild始終指向childNodes[0];lastChild指向childNodes[someNode.childNodes.length - 1];
var mydiv = document.getElementById("mydiv");
var firstChild = mydiv.firstChild;
var lastChild = mydiv.lastChild;
console.log(firstChild);
console.log(lastChild);
注:當只有一個子節點的情況下,firstChild和lastChild指向同一個節點;如果沒有子節點,均為null;并不是每種節點都有子節點;
<div id="mydiv"><h2>零點<span>程序員</span></h2></div>
<script>
var textChild = mydiv.firstChild.firstChild;
console.log(textChild.nodeType); // 3 文本節占
console.log(textChild.childNodes); // 空的NodeList[]
</script>
textChild指的是零點,而不是零點<span>程序員</span>;
ownerDocument 屬性:
指向這個節點所屬的文檔節點(也就是頂層節點);
任何節點都屬于它所在的文檔,任何節點都不能同時存在于兩個或多個文檔中,通過這個屬性不必層層回溯到頂端,而是直接訪問到文檔節點;
var mydiv = document.getElementById("mydiv");
console.log(mydiv.ownerDocument); // #document
var innerDiv = document.getElementById("innerDiv");
console.log(innerDiv.ownerDocument); // #document
console.log(mydiv.ownerDocument.documentElement); // <html>
節點層次關系圖:
節點層次關系圖
Node方法(操作節點):
appendChild(node) : 將node添加到childNodes的末尾; 添加節點后,childNodes的新添節點、父節點及以前的最后一個子節點的關系指針都會相應地得到更新;更新后,appendChild()返回新增的節點;
var mydiv = document.getElementById("mydiv");
var p = document.createElement("p");
p.innerText = "大師哥王唯";
var returnP = mydiv.appendChild(p);
console.log(mydiv.lastChild); // <p>大師哥王唯</p>
console.log(p === returnP); // true
如果傳入appendChild()的節點已經是文檔的一部分了,就將該節點從原來的位置轉移到新位置;即任何DOM節點不能同時出現在文檔中的多個位置上;
<script>
var mydiv = document.getElementById("mydiv");
var yourdiv = document.getElementById("yourdiv");
// 注:把最后的子元素的空白符刪除
var returnDiv = yourdiv.appendChild(mydiv.lastChild);
console.log(returnDiv === mydiv.lastChild); // false
console.log(returnDiv === yourdiv.firstChild); // true
</script>
如果在調用appendChild()方法時,傳入了父節點的第一個子節點,那么,該節點就會成為父節點中的最后一個子節點,如:
// 注意html中的換行空格
var mydiv = document.getElementById("mydiv");
console.log(mydiv.firstChild);
mydiv.appendChild(mydiv.firstChild);
console.log(mydiv.firstChild);
需要注意的問題;
var divs = document.getElementsByTagName("div");
var btn = document.createElement("input");
btn.type = "button";
btn.value = "按鈕";
for(var i=0; i<divs.length; i++){
console.log(divs[i]);
divs[i].appendChild(btn);
}
本來的意圖是為了給所有的div添加input子元素,可卻終只是最后的div添加了,原因是一個元素只能有一個父元素,起先,第一個div的確添加了input的元素,便是循環中的appendChild()會讓元素從原來的位置轉移到新位置;
改寫:把創建btn的代碼放到for循環內,即可達到目的;
由于appendChild()返回的是被追加的子元素,所以在鏈式調用時不能隨便使用;
var elt = document.createElement('p').appendChild(document.createElement('b'));
console.log(elt); // elt為<b></b>
本意是返回一個p節點,并且這個p元素包含一個b的子節點,但實際上elt為b;
// 改成
var elt = document.createElement('p')
elt.appendChild(document.createElement('b'));
console.log(elt); // <p>
document.body.appendChild(elt);
insertBefore(newnode, refnode)方法:
在childNodes中的refnode之前插入newcode,并返回這個節點;
插入節點后,被插入的節點(新節點)會變成參照節點的前一個兄弟節點;如果參照節點是null,則與appendChild()執行相同的操作;
var mydiv = document.getElementById("mydiv");
var p = document.createElement("p");
p.innerText = "大師哥王唯";
// 或者參照mydiv.firstChild節點
var returnP = mydiv.insertBefore(p,null); // 插入后成為最后一個子節點
console.log(mydiv.lastChild); // <p>大師哥王唯</p>
var returnP = mydiv.insertBefore(p, mydiv.firstChild); // 插入后成為第一個子節點
console.log(mydiv.firstChild); // <p>大師哥王唯</p>
var returnP = mydiv.insertBefore(p, mydiv.lastChild); // 插入到最后子節點的前面
console.log(mydiv.childNodes[mydiv.childNodes.length - 2]);// <p>大師哥王唯</p>
var returnP = mydiv.insertBefore(p,mydiv.childNodes[1]); // 插入到任意節點的前面
console.log(mydiv.childNodes[1]); // <p>大師哥王唯</p>
var yourdiv = document.getElementById("yourdiv");
yourdiv.insertBefore(mydiv.childNodes[1],null);//把mydiv的子節點插入到yourdiv中
參照節點refnode是必選參數,如果沒有,則傳null,否則會拋出異常;如果傳遞undefined,則會隱式轉換;
// mydiv.insertBefore(elt); // 異常
mydiv.insertBefore(elt,undefined); // undefined隱式轉換
// mydiv.insertBefore(elt,"undefined"); // 異常
如果給定的子節點是文檔中現有的節點,insertBefore() 會將其從當前位置移動到新位置;
var elt = document.getElementById("elt");
var mydiv = document.getElementById("mydiv");
mydiv.insertBefore(elt,mydiv.firstChild); // elt被移到mydiv內的第一個位置
提供一個使用索引位置插入節點的簡單函數;
// 將child節點插入到parent中,使其成為第n個子節點
function insertAt(parent, child, index){
if(index < 0 || index > parent.childNodes.length)
throw new Error("invalid index.");
else if(index == parent.childNodes.length)
parent.appendChild(child);
else
parent.insertBefore(child, parent.childNodes[index]);
}
var mydiv = document.getElementById("mydiv");
insertAt(mydiv,document.createTextNode("零點程序員"),mydiv.childNodes.length);
沒有insertAfter()方法,不過,可以使用insertBefore()和nextSibling()來模擬它;
// 如果refNode就是最后一個子節點,那么refNode.nextSibling為null
Element.prototype.insertAfter = function(newNode,refNode){
this.insertBefore(newNode, refNode.nextSibling);
}
mydiv.insertAfter(elt,mydiv.firstChild); // 插入到第二個位置
mydiv.insertAfter(elt,mydiv.lastChild); // 插入到最后
removeChild(node)方法:
移除子節點,并返回被移除節點
var mydiv = document.getElementById("mydiv");
var deleteChild = mydiv.removeChild(mydiv.firstChild);
console.log(deleteChild);
// 刪除的節點還可以再次使用
var yourdiv = document.getElementById("yourdiv");
yourdiv.appendChild(deleteChild);
console.log(yourdiv.firstChild);
被移除的節點仍然還在文檔中,但它在文檔中已經沒有了自己的位置,但如果被刪除節點沒有變量引用它,在一定的時間內將會被內存管理器回收;
但如果被刪除的子節點,一開始就被變量引用,即使該節點被刪除,其還被保持著引用;
var mydiv = document.getElementById("mydiv");
var deleteChild = mydiv.removeChild(mydiv.firstChild);
console.log(deleteChild === mydiv.firstChild); // false
// 改成
var deleteChild = mydiv.firstChild;
var returnChild = mydiv.removeChild(deleteChild);
console.log(deleteChild === returnChild); // true
removeChild()方法是在父節點上調用的,所以刪除一個節點,一定先定位好父節點再刪除子節點,比如要刪除當前的元素,可以:
node.parentNode.removeChild(node);
刪除所有子節點;
var mydiv = document.getElementById("mydiv");
for(var i=mydiv.childNodes.length-1; i>=0; i--){
console.log(mydiv.childNodes[i]);
mydiv.removeChild(mydiv.childNodes[i]);
}
console.log(mydiv);
為什么由后往前刪除,因為childNodes返回的列表是動態的,每一次訪問它都是被刪除一個后的列表;因此,如果只是單純的刪除所有子節點,可以:
// 更簡單的刪除,或者把firstChild換成lastChild也可以
while(mydiv.firstChild)
mydiv.removeChild(mydiv.firstChild);
replaceChild(newnode, oldnode) 方法:
刪除一個子節點并用一個新的節點取而代之,即將childNodes中的oldnode 替換成newnode;
會返回被替換的節點oldnode,并從文檔樹中刪除;
<div id="yourdiv">temp</div>
<script>
var mydiv = document.getElementById("mydiv");
var p = document.createElement("p");
p.innerText = "大師哥王唯";
var child = mydiv.replaceChild(p, mydiv.firstChild);
console.log(child);
var yourdiv = document.getElementById("yourdiv");
if(yourdiv.hasChildNodes()){
var child = yourdiv.replaceChild(mydiv.lastChild,yourdiv.firstChild);
}
</script>
定義一個環繞節點的函數:
function roundNode(outerNodeString,innerNode){
// 假如參數為字符串而不是節點,將其當做元素的id
if(typeof innerNode == "string")
innerNode = document.getElementById(innerNode);
var parent = innerNode.parentNode; // 取得父節點
var outerNode = document.createElement(outerNodeString);
parent.replaceChild(outerNode, innerNode);
outerNode.appendChild(innerNode);
return outerNode;
}
var mydiv = document.getElementById("mydiv");
roundNode("div",mydiv).style.color = "red";
cloneNode()方法:
用于創建調用這個方法的節點的一個完全相同的副本;
其接受一個參數,表示是否執行深復制,true為深復制,即復制節點及其整個子節點樹; false,則為淺復制,即只復制節點本身,節點所包含的文本也不會被復制,默認為false;
var mydiv = document.getElementById("mydiv");
var cloneDiv = mydiv.cloneNode(true);
console.log(cloneDiv);
或
<ul id="mylist">
<li>HTML5</li>
<li>CSS3</li>
<li>Javascript</li>
</ul>
<script>
var myList = document.getElementById("mylist");
var deepList = myList.cloneNode(true);
var shallowList = myList.cloneNode(false);
console.log(deepList.childNodes.length); // 7
console.log(shallowList.childNodes.length); // 0
</script>
復制后的節點副本屬于文檔所有,但并沒有父節點,因此需要通過appendChild()、insertBefore或replaceChild將它添加文檔中;
console.log(mydiv.parentNode); // <body>
console.log(cloneDiv.parentNode); // null
var yourdiv = document.getElementById("yourdiv");
yourdiv.appendChild(cloneDiv); // 不符合標準,因為有兩個id為mydiv元素
console.log(cloneDiv.parentNode); // <div id="yourdiv">
cloneNode()方法不會復制添加到DOM節點中的Javascript屬性,如事件處理程序等,但會復制特性、子節點;但如果是在標簽中直接添加的on事件,也會被復制,因為它是被當作特性復制的;
var myList = document.getElementsByTagName("ul")[0];
var li3 = myList.childNodes[5]; // 第3個li
li3.addEventListener("click",function(e){
alert(this.innerText);
},false);
var deepList = myList.cloneNode(true);
var yourdiv = document.getElementById("yourdiv");
yourdiv.appendChild(deepList);
如果原始節點設置了ID,并且克隆節點會被插入到相同的文檔中,那么就應該修改克隆節點的ID以保證其唯一性;
console.log(deepList.id); // mylist
deepList.id = "deeplist";
console.log(deepList.id); // deeplist
normalize()方法:
合并節點,該方法唯一的作用就是處理文檔樹中的文本節點;由于解析器的實現或DOM操作等原因,可能會出現文本節點不包含文本,或者接連出現兩個文本節點的情況;當在某個節點上調用該方法時,就會在該節點的后代節點中查找上述兩種情況;如果找到了空文本節點,則刪除它;如果找到相鄰的文本節點,則將它們合并為一個文本節點;
hasChildNodes()方法:
判斷節點是否有子節點,會返回一個布爾值;
var mydiv = document.getElementById("mydiv");
console.log(mydiv.childNodes.length);
console.log(mydiv.hasChildNodes());
if(mydiv.hasChildNodes())
console.log("有子節點");
else
console.log("無");
// 如果源文件中有空白符,刪除的是空白文本節點
if(mydiv.hasChildNodes())
mydiv.removeChild(mydiv.firstChild);
// 全部刪除
while(mydiv.hasChildNodes())
mydiv.removeChild(mydiv.firstChild);
總結:判斷一個節點是否有子節點,有三種方式;
node.firstChild !== null、node.childNodes.length > 0、node.hasChildNodes()方法;
contains()方法:
在實際開發中,經常需要知道某個節點是不是另一個節點的后代; 其接受一個后代節點,用于判斷是否為當前元素的后代節點,如果是,返回true,否則為false;
console.log(document.documentElement.contains(document.body));
// 檢查一個元素是否是body元素的后代元素且非body元素本身
function isInPage(node){
return (node === document.body) ? false : document.body.contains(node);
}
console.log(isInPage(mydiv));
Web前端開發之Javascript-零點程序員-王唯
*請認真填寫需求信息,我們會在24小時內與您取得聯系。