JavaScript基礎入門08
JavaScript 基礎入門08
DOM
介紹
JavaScript 三部分當中,DOM
占據了很大的一部分,當js的宿主環境為瀏覽器時,DOM才可以使用。DOM,即Document Object Model,也就是文檔對象模型。
DOM是操作網頁的基礎API。通過DOM,我們可以非常方便的操作網頁當中的內容。
綁定事件
我們之間學習過給一個元素綁定事件。
var btn = document.getElementById("btn");
btn.onclick = function () {
// code ...
}
也可以直接將事件處理函數寫在元素的內部。
<button onclick = "fn1()">點擊</button>
在js中,除了上述的兩種綁定事件的方式以外,還可以采用事件監聽
的形式來進行事件綁定。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>DOM事件綁定</h1> <hr> <button id="btn">點擊</button> </body> <script> function fn1() { alert(123); } var btn = document.getElementById("btn"); btn.addEventListener('click',fn1); </script> </html>
上面代碼中,addEventListener
方法是w3c
推薦使用的綁定事件的方式。第一個參數為事件,需要註意的是事件名稱不加on,第二個參數是事件處理函數,不需要再函數的後面添加括號,否則函數會自動執行。
我們可以通過事件監聽來給一個元素綁定多個相同事件處理函數,並且當觸發事件後,多個事件處理函數都會執行。
事件監聽
的方法並不支持IE9以下,為了完成兼容,在ie9及以下版本,可以采用attachEvent()
方法。
通常,我們可以采用類似於下面的代碼進行兼容處理:
if (btn.addEventListener) { btn.addEventListener('click',fn); }else { btn.attachEvent('onclick',fn); }
也可以將內容封裝成函數:
function bindEvent(dom,event,fn) {
if (dom.addEventListener){
dom.addEventListener(event,fn);
}else if(dom.attachEvent){
dom.attachEvent("on" + event,fn);
}else {
console.log("對不起,您的瀏覽器不支持。");
}
}
給一組元素綁定事件
當我們碰到需要給一組元素綁定事件的時候,可以采用類似於下面代碼的寫法:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<ul id="list">
<li>aaaaa</li>
<li>aaaaa</li>
<li>aaaaa</li>
<li>aaaaa</li>
<li>aaaaa</li>
<li>aaaaa</li>
</ul>
</body>
<script>
var lis = document.getElementById('list').getElementsByTagName('li');
for(var i=0;i<lis.length;i++){
lis[i].onclick = function (){
this.style.background = 'red';
}
}
</script>
</html>
節點
DOM當中,最小的組成單位是節點(node)。文檔的樹形結構也就是我們常說的DOM樹,就是由不同類型的節點組成。
通常,節點的類型可以分為下面的幾種:
- Document :整個文檔樹的頂層節點
- DocumentType : doctype標簽(比如
<!DOCTYPE html>
) - Element : 網頁的各種html標簽
- Attribute:網頁元素的屬性(比如
class=‘test‘
) - Text: 標簽之間或者標簽包含的文本
- Comment: 註釋
- DocumentFragment : 文檔的片段
瀏覽器提供了一個原生的節點對象Node
,上面的這七種節點都繼承了Node,因此具備一些相同的屬性和方法。
節點樹
一個文檔的所有節點,按照所在的層級,可以抽象成一種樹狀結構。這種樹狀結構就是 DOM 樹。它有一個頂層節點,下一層都是頂層節點的子節點,然後子節點又有自己的子節點,就這樣層層衍生出一個金字塔結構,倒過來就像一棵樹。
瀏覽器原生提供document
節點,代表整個文檔。
document
// 整個文檔樹
文檔的第一層有兩個節點,第一個是文檔類型節點(<!doctype html>
),第二個是 HTML 網頁的頂層容器標簽<html>
。後者構成了樹結構的根節點(root node),其他 HTML 標簽節點都是它的下級節點。
除了根節點,其他節點都有三種層級關系。
- 父節點關系(parentNode):直接的那個上級節點
- 子節點關系(childNodes):直接的下級節點
- 同級節點關系(sibling):擁有同一個父節點的節點
DOM 提供操作接口,用來獲取這三種關系的節點。比如,子節點接口包括firstChild
(第一個子節點)和lastChild
(最後一個子節點)等屬性,同級節點接口包括nextSibling
(緊鄰在後的那個同級節點)和previousSibling
(緊鄰在前的那個同級節點)屬性。
節點類型
我們可以通過nodeType
屬性查看節點的類型,節點的類型一共包括下面的幾種類型:
不同節點的nodeType
屬性值和對應的常量如下。
- 文檔節點(document):9,對應常量
Node.DOCUMENT_NODE
- 元素節點(element):1,對應常量
Node.ELEMENT_NODE
- 屬性節點(attr):2,對應常量
Node.ATTRIBUTE_NODE
- 文本節點(text):3,對應常量
Node.TEXT_NODE
- 文檔片斷節點(DocumentFragment):11,對應常量
Node.DOCUMENT_FRAGMENT_NODE
- 文檔類型節點(DocumentType):10,對應常量
Node.DOCUMENT_TYPE_NODE
- 註釋節點(Comment):8,對應常量
Node.COMMENT_NODE
選取文檔內容
DOM定義了許多方式來選區或者查詢一個或者多個文檔元素,如下:
用指定的id屬性
用指定的標簽名
用指定的css類
用指定的name屬性
匹配指定的css選擇器
通過id選取元素
通過元素的id屬性我們可以選取到元素,通過document.getElementById()
,能夠準確的找到元素。
var btn = document.getElementById('btn');
如果需要通過id選取一組元素,可以采用類似於下面的代碼:
function getElements(/*ids*/) {
var elements = {};
for(var i=0;i<arguments.length;i++){
var id = arguments[i];
var elt = document.getElementById(id);
if(elt === null) {
throw new Error("No element with id:" + id);
}
elements[id] = elt;
}
return elements;
}
Tip:在低於IE8版本的瀏覽器中,getElementById()對匹配元素的id不區分大小寫,而且也返回匹配name屬性的元素。
通過指定的標簽名選取元素
getElementsByTagName()
方法可以用來選取指定類型的(標簽名)所有的HTML元素或者XML元素。
如:
var test = document.getElementsByTagName(‘p’)[0];
Tip:html標簽不區分大小寫,在html文檔中使用getElementsByTagName()時,將進行不區分大小寫的標簽名比較。
Tip:給getElementsByTagName()傳遞通配符*,將獲得頁面中的所有的元素。
如果我們要獲取一個元素內的子元素,也可以如下
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div>
<span>hello</span>
</div>
</body>
<script>
var div = document.getElementsByTagName('div');
var span = div.getElementsByTagName('span');
</script>
</html>
用指定的css類來選取元素
HTML元素的class屬性值是一個以空格隔開的列表,可以為空或者包含多個標符。
在js中采用className屬性來保存HTML的class屬性值。
使用方法:var one = document.getElementsByClassName(‘class值’);
例如:
<script>
var div = document.getElementsByClassName('d1 no'); // 選中class類名中包含d1 和no的標簽
var no = document.getElementsByClassName('no');// 匹配了class類名中帶有no的標簽
</script>
這個方法只能夠使用一個字符串參數,但是這個字符串參數可以由多個空格隔開的標識符組成。只有當元素的class屬性值包含所有指定的標識符時才能匹配,但是順序無關緊要。
通過name屬性來選取元素
html 的name屬性最初是為表單元素分配名字,在表單數據提交到服務器時使用該屬性得值。類似於id。但是和id得區別是,name屬性的值不是必須唯一的,多個元素可以存在相同的名字,例如,單選和復選按鈕通常是這種情況。
並且,和id最大的區別在於,id是全局屬性,但是name只能夠在少數的html元素中有效,
這其中包括表單,表單元素,iframe 和 img元素。
用法:var t = document.getElementsByName(‘color’);
需要註意的是,在IE中,getElementsByName()也返回id屬性匹配的元素。我們為了兼容,應該小心,不要將name和id值設置為一致。
var input = document.getElementsByName('username');
通過CSS選擇器選取元素
在css中,我們可以通過選擇器來選取元素。而隨著css3選擇器的標準化,w3c推薦了另一個稱作”選擇器API”的方法,用以獲取匹配一個給定選擇器的元素方法----querySelectorAll()。
它接受一個字符串參數,這個參數中包含一個css選擇器。返回一個表示文檔中匹配選擇器的所有元素的NodeList對象。
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div></div>
<div></div>
<div></div>
</body>
<script>
var div = document.querySelectorAll('div');
console.log(div);
</script>
</html>
需要註意的是,這個方法返回的對象的內容並不是實時的:包含在調用的時刻選擇器所匹配的元素,但是並不包含在調用之後匹配的元素。如果沒有匹配的元素,將返回一個空的對象。如果選擇器異常,將拋出一個異常。
與其相似的方法有querySelector()
。這個方法與上面的類似,但是只返回第一個匹配的元素(文檔順序)。如果沒有匹配的元素就返回null。
var div = document.querySelector('div');
console.log(div);
Tip:
在css3中,新增了兩個選擇器::first-line 和 :first-letter 等偽元素。這種偽元素實際上匹配的文本節點的一部分,而不是實際的元素。那麽當我們使用以上的兩種方法的時候,就不會匹配成功。
而且,在很多的瀏覽器中拒絕返回:link 和:visited等偽類的匹配結果,認為這樣會泄露用戶的瀏覽信息。
文檔結構和遍歷
一旦選取了文檔中的一個元素,有時我們需要查找文檔中與之在結構上相關的部分(父親,兄弟,子女)。針對此種問題,js給了我們兩個API使用,節點對象樹API 和 元素對象樹API。
節點對象樹API -- 作為節點樹的文檔
Node定義的重要屬性:
parentNode 找到節點的父節點
childNodes 該節點的子節點
firstChild lastChild 該節點中的子節點的第一個或者最後一個
nextSibling previoursSibling 該節點的兄弟節點的前一個和下一個
nodeValue text節點或者Comment節點的文本內容
nodeName 元素的標簽名,以大寫的形式顯示。
Tip:這個API對文檔文本的變化非常敏感,需要小心。
元素對象樹API -- 作為元素樹的文檔
這個API所提供的方法會將文檔看做Element文檔樹,會忽略掉Text和Comment節點。
這個API有兩部分組成:
第一部分:children 屬性
第二部分:Element 屬性
element屬性內容:
firstElementChild lastElementChild
nextElementSibling perviousElementSibling
childElementCount 子元素的數量,返回值和childred.length相等
屬性
HTML元素由一個標簽和一組屬性組成。例如a元素定義了超鏈接,href屬性值則定義鏈接地址。DOM定義了另一個API來獲取或者設置html屬性。
獲取和設置HTML屬性
Element類型定義了getAttribute()和 setAttribute()方法來查詢和設置非標準的HTML屬性,也可以用來查詢和設置XML文檔中元素上的屬性。
getAttribute() 只有一個字符串參數,你打算查詢的屬性的名字
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div class="test" title="hello,world">hello,world</div>
</body>
<script>
var div = document.getElementsByTagName('div')[0];
console.log(div.getAttribute('class')); // test
console.log(div.getAttribute('title')); // hello,world
</script>
</html>
setAttribute()
修改屬性節點的值 有兩個參數,第一個參數是要設置的屬性名
第二個參數是要設置的屬性值。
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div class="test" title="hello,world">hello,world</div>
</body>
<script>
var div = document.getElementsByTagName('div')[0];
var info = div.setAttribute('title','this is title....');
console.log(info); // undefined
</script>
</html>
雖然以上的代碼在大部分的瀏覽器中很好用,但是放在IE中不是很好用,如以下代碼,在IE7中會失效。
hasAttribute()檢測命名屬性是否存在
var d1 = document.getElementById('div');
d1.hasAttribute('title'); // true | false
removeAttribute()完全刪除屬性 兼容性良好,無返回值。
var d1 = document.getElementById('div');
d1.removeAttribute('title');
數據集屬性
有時候在H*TML**元素上綁定一*些額外的信息也是很有幫助的,當javascript選取這些元素並且以某種方式操縱這些信息的時候就是很典型的情況 。有時候可以通過給class屬性添加特殊的標識符來完成。其他的時候針對更復雜的數據,客戶端程序員會借助使用非標準的屬性。對於非標準的屬性,可以通過使用getAttribute()和setAttribute()來讀和寫非標準屬性的值。而代價是html文檔不在是合法有效的文檔。
而HTML5提供了一個解決方案,在HTML5中,任意以”data-”為前綴的小寫的屬性名字都是合法的。這些”數據集屬性”將不會對其元素的表現產生影響,他們定義了一種標準的、附加額外數據的方法,並不是在文檔合法性上作出讓步。
針對這種情況,HTML5提供了一種解決方案,在Element對象上定義了dataset屬性。
這個屬性指代一個對象,它的各個屬性對應於去掉前綴的data-屬性。因此dataset.x應該保存data-x屬性的值。帶連字符的屬性對應於駝峰命名法屬性名:data-jquery-test 屬性就變成了dataset.jqueryTest屬性。
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Index</title>
</head>
<body>
<div id="d1" data-name="test" data-hobby-nav="small test...">這是一個測試.....</div>
</body>
<script>
var a = document.getElementById('d1');
var name = a.dataset.name;
console.log(name); // test
var test = a.dataset.hobbyNav;
console.log(test); // small test ...
</script>
</html>
Tip:如果你賦值的data-* 不存在,將會自動創建.
在html5中還有一個與之類似的對象--classList我們可以通過這個對象裏面的方法實現新增、刪除、修改節點上的css類,還可以判斷某個節點是否被賦予了某個css類.
Element.classList裏面存在著很多有用的方法:
- length
- add
- contains
- item
- remove
- toggle
mDiv.classList.add(‘myCssClass’);
var info = document.getElementById('div');
info.classList.add('test');
myDiv.classList.remove(‘myCssClass’);
var info = document.getElementById('div');
info.classList.add('test');
var main = document.getElementsByTagName('div')[1];
main.classList.remove('ceshi');
Tip:可以在字符串內傳入多個類名,但是結果有可能非預期。
myDiv.classList.toggle(‘myCssClass’);//增加css類myDiv.classList.toggle(‘myCssClass’);//刪除css類
Tip:作用是當元素沒有這個css類時,就新增這個css類。如果已經有了這個css類,就刪除它。這就是反轉操作。
myDiv.classList.contains(‘myCssClass’);檢查是否含有某個css類
作為Attr節點的屬性
還有一種使用Element的屬性的方法。Node類型定義了attributes屬性。針對非Element對象的任何節點,該屬性為null。對於Element對象,attributes屬性是只讀的類數組對象,它代表元素的所有的屬性。attributes對象是實時的。可以用數字索引進行訪問,這就意味著可以枚舉元素的所有屬性。並且也可以用屬性名索引。
例如:
<div class="test" id="ceshi" title="hello,world">測試....</div>
<script>
var div =document.getElementById('ceshi');
var info = div.attributes['title'];
console.log(info.name);//顯示屬性名
console.log(info.value); //顯示屬性值
</script>
使用DocumentFragment
DocumentFragment是一個特殊的節點,作為其他節點的一個臨時容器。
可以如下寫法:
var frag = document.createDocumentFragment();
就如同Document節點一樣,DocumentFragment也是獨立的,而不是任何其他的文檔的一部分。它的parentNode總是為Null。
但是類似Element,它可以有任意多的子節點,可以使用appendChild() 、insertBefore()等方法來操作他們。
DocumentFragment的特殊之處在於,它使得一組節點被當作一個節點看待:如果給appendChild() insertBefore()或者replaceChild()傳遞一個DocumentFragment,其實是將該文檔片段的所有子節點插入到文檔中,而非片段本身。
例如:
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<ul id="list"></ul>
<script>
var frag = document.createDocumentFragment();
console.log(frag);
for(var x=0;x<10;x++){
var li = document.createElement("li");
li.innerHTML = "list item" + x;
frag.appendChild(li);
}
var lists = document.getElementById('list');
lists.appendChild(frag);
</script>
</body>
</html>
小練習
內容發布:
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title></title>
<style>
ul{
list-style-type: none;}
*{margin:0;padding: 0;}
.box {
margin: 100px auto;
width: 600px;
height: auto;
border:1px solid #333;
padding: 30px 0;
}
textarea {
width: 450px;
resize: none; /*防止用戶拖動 右下角*/
}
.box li {
width: 450px;
line-height: 30px;
border-bottom:1px dashed #ccc;
margin-left: 80px;
}
.box li a {
float: right;
}
</style>
<script>
window.onload = function(){
//獲取對象 再次操作對象
var btn = document.getElementsByTagName("button")[0];
var txt = document.getElementsByTagName("textarea")[0];
var ul = document.createElement("ul"); // 創建ul
btn.parentNode.appendChild(ul); // 追加到 他的父親裏面
btn.onclick = function(){
if(txt.value == "")
{
alert("輸入不能為空");
return false; // 終止函數執行
}
var newli = document.createElement("li");
newli.innerHTML = txt.value + "<a href ='javascript:;'>刪除</a>"; // 吧表單的值給 新li
txt.value = ""; // 清空 表單
var lis = ul.children; // 獲得有多少個li
// if else 這個片段 讓我們新發布的內容 最上顯示
if(lis.length == 0) // 第一次創建
{
ul.appendChild(newli); // ul 的後面追加
}
else
{
ul.insertBefore(newli,lis[0]); // 每次生成的新的li 放到第一個li 的前面
}
var as = document.getElementsByTagName("a"); // 獲得所 a
for(var i=0; i<as.length;i++)
{
as[i].onclick = function(){
//removeChild
ul.removeChild(this.parentNode); // UL 他的爸爸
// a 的父級是 li
}
}
}
}
</script>
</head>
<body>
<div class="box">
內容發布: <textarea name="" id="" cols="30" rows="10"></textarea> <button>發布</button>
</div>
</body>
</html>
JavaScript基礎入門08