JS DOM和BOM
1.DOM
DOM就是文件物件模型,什麼是文件物件模型?這就需要好好說說了。
HTML的文件document頁面是一切的基礎,沒有它dom就無從談起。
當建立好一個頁面並載入到瀏覽器時,DOM就悄然而生,它會把網頁文件轉換為一個文件物件,主要功能是處理網頁內容。
在這個文件物件裡,所有的元素呈現出一種層次結構,就是說除了頂級元素html外,其他所有元素都被包含在另外的元素中。
那麼它的樹就應該是下面這樣的一顆倒長的樹:
2.節點型別
節點表示網路中的一個連線點,一個網路就是由一些節點構成的。
而文件就是由節點構成集合,只不過他們是構成節點樹上的樹枝樹葉而已。
這些節點有許多不同的型別,我們先來看看其中的三種:
元素節點、文字節點和屬性節點。
HTML的標籤元素就是DOM的元素節點,它提供了一份文件的結構。
但這份文件本身不會包含任何內容,因此元素節點可以包含其他的節點。
文字節點是節點型別的一種,它總是被包含在元素節點內部,形成頁面文件的主要內容。
屬性節點用於對元素做出個個具體的描述,例如:
a元素的href屬性,img元素的alt屬性。
屬性總是被放在起始標籤裡,因此屬性節點也總是被包含在元素節點中。
雖然並非所有的元素節點都包含屬性,但所有的屬性一定都被元素節點包含。
3.節點操作
3.1獲取元素節點
獲取元素節點有4種方法,分別通過元素ID,標籤名字,類名和css選擇器來獲取。
3.1.1 元素ID
getElementById方法是document物件特有的函式,傳入一個引數即元素的id屬性值,將返回一個物件。
這個物件對應那個id屬性為指定值的節點,我們還可以用typeof來驗證它的型別:
document.getElementById(“car”);
alert(typeof document.getElementById(“car”));
實際上文件中每一個元素都是一個物件,利用DOM提供的方法可以得到任意一個物件。
不過要是為每一個元素都定義一個獨一無二的ID值那就太麻煩了,所以DOM還提供了另外的方法來獲取沒有id的物件。
3.1.2 標籤名字
getElementsByTagName方法會返回一個物件陣列,陣列的元素就是和getElementById差不多的獲取到的物件:
document.getElementsByTagName(“li”);
還可以用陣列的方法length來獲取這個陣列的長度:
document.getElementsByTagName(“li”).length;
3.1.3 類名
getElementsByClassName方法讓我們能夠通過class類名來訪問元素。
它的返回值和getElementsByTagName類似,都是返回一個物件陣列:
document.getElementsByClassName(“sale”);
值得注意的是它還可以匹配含有多個class的元素,指定多個類名的時候
只需要在字串引數中間yoga空格分隔類名就可以了,順序不重要前後都可以。
另外它也可以和前面兩種方法混合使用,用法和getElementById和getElementsByTagName結合使用的例子一致。
3.1.4 CSS選擇器
還有html5中新增的兩個方法,讓我們可以用css選擇器的方法來選擇DOM節點,這兩個方法必須在IE8以上的現代瀏覽器中才能使用。
第一個方法是返回了單個節點,如果有多個匹配元素就只返回第一個,如果找不到匹配就返回。
第二個方法是返回一個節點列表集合。引數則都為CSS選擇器字串:
document.querySelector(“#foo");
document.querySelectorAll(“.bar");
3.2 獲取和設定屬性
在得到元素後,就可以獲取他們的屬性,然後更改屬性的值了:
3.2.1 getAttribute
getAttribute函式是一個屬於節點物件的方法,可以通過傳入引數獲取節點物件下的各種屬性:
var p = document.getElementById(“parse”);
var title = p.getAttribute(“title”);
alert(title);
3.2.2 setAttribute
setAttribute方法與getAttribute對應,它不再是獲取,而是修改,因此它的引數有兩個:
第一個是屬性名,第二個是想要修改為的值:
var p = document.getElementById(“parse”);
p.setAttribute(“title”, “nice day!”);
alert(p.getAttribute(“title”));
值得注意的是,這個時候如果去檢視原始碼,會發現P元素的title屬性值並沒改變。
這是因為DOM的工作模式是:
先載入靜態內容,再動態重新整理,動態重新整理不影響文件的靜態內容。
3.3 在樹上爬行
childNodes,在一顆節點樹上,這個屬性可以用來獲取一個元素的所有子元素,得到一個包含所有子元素的陣列:
element.childNodes
// 如果要獲得body元素下的全體子元素
document.getElementsByTagName(“body”)[0].childNodes;
parentNode,獲取當前節點的父節點元素,如果指定節點沒有父元素那麼會返回null。
nodeType, 每個節點都有nodeType屬性,這個屬性是一個數值,讓我們可以知道正在處理哪種型別的節點。
節點型別有十多種,但其中我們最需要了解的有3種:
元素節點的nodeType屬性值是1
屬性節點的nodeType屬性值是2
文字節點的nodeType屬性值是3
這就意味著我們可以只對特定型別的節點進行處理,比如只處理元素節點。
alert(node.nodeType);
nodeValue,如果想改變文字節點的值,就可以使用這個屬性:
node.nodeValue;
比如當有一個p元素節點,裡面有一些文字內容,如果想取得這些文字內容,那麼直接對p元素使用nodeValue會得到一個null空值。
因為這樣得到的是p元素節點的值而不是它的子元素文字節點的值,因此可以這樣來得到真正需要的內容:
p.childNodes[0].nodeValue;
firstChild和lastChild,是對子元素陣列更簡易操作的屬性,就是childNodes的第一個和最後一個,這樣更簡短也更有可讀性。
3.4 動態建立
前面的方法都是對已經存在的元素做出搜尋和修改。
然而js也可以用來改變網頁的結構和內容,可以通過建立新元素和改變現有元素來改變網頁結構。
3.4.1 傳統方法
document.write()方法可以方便快捷的把字串插入到文件中
innerHTML屬性可以用來讀寫html的內容
3.4.2 DOM操作法
如果想把一段文字內容放到p元素中,然後將p元素插入到頁面的某個節點後,那麼這個任務可以分為幾個步驟:
a.建立一個p元素節點
b.把這個p元素節點最佳到文件中的#parent元素節點上
c.建立一個文字節點
d.把這個文字節點追加到剛才建立的p元素節點上
var para = document.createElement(“p”);
var parent = document.getElementById(“parent”);
parent.appendChild(para);
var txt = document.createTextNode(“hello world”);
para.appendChild(txt);
來認識一下這幾個方法:
createElement,建立一個元素節點,傳入的引數就是標籤名字串。但它只是一個文件碎片,還不是DOM節點樹上的組成部分,無法顯示在瀏覽器裡。
createTextNode,建立一個文字節點用於放文字內容,和上面幾乎一樣,只是傳入的引數就是文字字串,建立好後依舊是文件中的一個遊蕩的孤兒。
appendChild,想把新建立的節點插入節點樹的最簡單辦法之一,讓它成為某個節點的一個子節點。
insertBefore,這個方法可以在已有元素前插入一個新元素。
呼叫這個方法時必須知道三件事:新元素和目標元素和父元素,其語法是這樣的:
parentElement.insertBefore(newElement, targetElement);
比如想在一個img圖片前插入一個p標籤,那麼可以這樣做:
var img = document.getElementById(“img”);
var p = document.getElementById(“p”);
img.parentNode.insertBefore(p, img);
4.操作封裝
前面說到過insertBefore方法,那是不是也該有個對應的insertAfter()呢,很遺憾,沒有。。。
那麼我們其實可以自己實現一個這樣的方法:
function insertAfter(newEle, targetEle) {
var parent = targetEle.parentNode;
if (parent.lastChild == targetEle) {
parent.appendChild(newEle);
} else {
parent.insertBefore(newEle, targetEle.nextSibling);
}
}
它的實現步驟是這樣的:
a.首先,這個函式有兩個引數,一個是將被插入的元素,一個是目標元素。
b.把目標元素的父元素儲存到變數parent裡
c.檢查目標元素是不是父元素parent的最後一個子元素
d.如果是,就用appendChild方法把新元素追加到父元素parent上,這樣新元素就恰好被插入到目標元素之後
e.如果不是,就把新元素插入到目標元素的下一個兄弟元素之前,這樣新元素就在目標元素之後
通過這樣一個函式,加上已知的幾個方法,就能自己封裝出自己所需要的方法了。
5.BOM
BOM,browser object model,瀏覽器物件模型,這個物件就是對應著瀏覽器視窗window。
它提供了一些方法用於訪問瀏覽器的功能,這些功能和網頁內容無關。
5.1 window物件
window物件是BOM的核心,表示瀏覽器正開啟的視窗,它是一個全域性物件。
它還有一些屬性方法和子物件,我們其實已經默默的使用過它了。
比如alert()方法,但因為它是widow物件的直接後代,所以不需要加上window字首。
另外我們定義的全域性變數,其實也是定義到了window上的。
5.1.1 全域性作用域
var car = “Tesla”;
function run() {
alert(this.car);
};
alert(window.car);
run();
window.run();
在全域性作用域中定義了變數car和函式run,它們會自動歸為window物件的,因此可以通過window點來訪問它們。
另外run函式存在於全域性作用域中,因此this也被指向window。
5.1.2 系統對話方塊
alert(), confirm(), prompt()這幾個方法可以呼叫系統對話方塊向用戶顯示訊息。
alert生成一個警告框,用於顯示一些使用者無法控制的訊息,看過後只能關閉了事。
confirm生成的對話方塊和前者的不同在於多一個cancel按鈕,可以讓使用者決定是否執行,並返回一個布林值
true表示點選了確定,false表示點選了取消。
prompt則是生成一個提示框,用於提示使用者輸入一些文字內容,這個方法接受2個引數:
文字提示和輸入框的預設值。使用者操作點選ok後,返回文字框輸入的值;
如果點選cancel或者直接關閉,則會返回null。
5.1.3 視窗操作
5.2 location物件
這個物件讓我們可以訪問當前載入的URI(統一資源識別符號)的任意資訊
//如果想查詢字串引數,用已有的屬性並不方便,因此可以建立這樣一個函式來解析查詢字串
function getQueryStringArgs(){
//取得查詢字串並去掉開頭的問號
var qs = (location.search.length > 0 ? location.search.substring(1) : "”),
//儲存資料的物件 args = {},
//取得每一項
items = qs.length ? qs.split("&") : [], item = null,
name = null,
value = null,
//在 for 迴圈中使用
i = 0,
len = items.length;
//逐個將每一項新增到 args 物件中
for (i=0; i < len; i++){
item = items[i].split("=");
name = decodeURIComponent(item[0]);
value = decodeURIComponent(item[1]);
if (name.length) {
args[name] = value;
}
}
return args;
}
//假設查詢字串是?q=javascript&num=10
var args = getQueryStringArgs();
alert(args["q"]); //"javascript"
alert(args["num"]); //"10"
可以通過location的屬性來改變當前url,當然下面這兩種寫法是一樣的效果:
window.location = “www.baidu.com”;
location.href = “www.baidu.com”;
另外也能使用location的屬性改變URL,以重新載入
5.3 navigator物件
這個物件提供幾個屬性,用於輔助檢測瀏覽器環境
因為js經常做的事情之一就檢測使用者正在使用哪種瀏覽器。
不過這個物件的屬性非常多,可以在瀏覽器的除錯工具中直接打出來看看它的屬性和方法
5.4 history物件
這個物件提供了通過使用者訪問產生的瀏覽歷史來向前或向後移動的方法。
用go()方法可以在歷史記錄中任意跳轉,可以向前也可以向後
這個方法接受一個引數,表示向前或向後頁面數的一個整數,負值表示向後,正數表示向前。
另外還有兩個簡寫方法back和forward來替代go,只是每次只能前進或後退一頁,無需引數:
//後退一頁
history.go(-1);
history.back();
//前進一頁
history.go(-1);
history.forward();
//後退兩頁
history.go(-2);
(內容來自