從零開始學(xué)習(xí)BOM&DOM

前言

ECMAScript,描述了該語言的語法和基本對(duì)象,如類型、運(yùn)算、流程控制、面向?qū)ο?、異常等?/p>

文檔對(duì)象模型(DOM),描述處理網(wǎng)頁內(nèi)容的方法和接口。

瀏覽器對(duì)象模型(BOM),描述與瀏覽器進(jìn)行交互的方法和接口。

DOM/BOM架構(gòu)

image

概述

JavaScript運(yùn)行在瀏覽器

BOM就是連接JavaScript代碼和瀏覽器的橋梁,而DOM就是用來操作各種標(biāo)簽元素的。

  • BOM包括 window、history、location、document ...
  • DOM包括 Document(整個(gè)文檔)、Element(標(biāo)簽元素)、CharacterData(字符數(shù)據(jù))、Attr(屬性),這些元素又可以繼續(xù)往下劃分。

    • Document 又可以分為HTMLDocument(就是我們定義的html文件)和XMLDocument(XML文件,安卓會(huì)用,前端用不到)

    • Element 可以劃分為 HTMLElement(表示 HTML 中的一個(gè)元素,比如div),HTMLElement又可以劃分為HTMLDIvElement 和 HTMLImageElement 故名思義

    • CharacterData 可以劃分為text(text標(biāo)簽)和Comment(注釋)

    • Attr 可以理解我們?cè)氐?strong>class屬性id屬性以及值

繼承關(guān)系

  • 其中DOM元素、window對(duì)象都繼承自EventTarget,所以它們都有EventTarget上的實(shí)例方法而document是HTMLDocument的實(shí)例對(duì)象
  • 所以window上是可以綁定事件,監(jiān)聽事件,分發(fā)事件的
  • 其中DOM 中的所有元素節(jié)點(diǎn)都繼承自EventTarget接口,所以DOM中任意節(jié)點(diǎn)可以綁定事件,監(jiān)聽事件,分發(fā)事件

我們可以在瀏覽器中打印它的原型屬性

通過下圖我們可以看出,window繼承Window,Window繼承自EventTarget

image

通過下圖我們可以看出,document繼承HtmlDocument,HtmlDocument繼承自Document,Document繼承自Node節(jié)點(diǎn),Node節(jié)點(diǎn)繼承自EventTarget

image

順便說一句:EventTarget繼承自O(shè)bject,Object繼承自null

EventTarget 接口

EventTarget 是一個(gè) DOM 接口,由可以接收事件、并且可以創(chuàng)建偵聽器的對(duì)象實(shí)現(xiàn)。

Element,documentwindow 是最常見的 event targets

EventTarget有三個(gè)原型方法,在window和DOM元素上都可以使用

  • addEventListener 給元素綁定事件

  • removeEventListener 移除元素綁定的事件

  • dispatchEvent 派發(fā)事件

BOM

認(rèn)識(shí)BOM

  • JavaScript有一個(gè)非常重要的運(yùn)行環(huán)境就是瀏覽器,而且瀏覽器本身又作為一個(gè)應(yīng)用程序需要對(duì)其本身進(jìn)行操作,所以通常瀏覽器會(huì)有對(duì) 應(yīng)的對(duì)象模型(BOM,Browser Object Model)。
  • BOM主要包括一下的對(duì)象模型:

    • window:包括全局屬性、方法,控制瀏覽器窗口相關(guān)的屬性、方法;

    • location:瀏覽器連接到的對(duì)象的位置(URL);

    • history:操作瀏覽器的歷史;

    • document:當(dāng)前窗口操作文檔的對(duì)象;

  • window對(duì)象在瀏覽器中有兩個(gè)身份:

    • 身份一:全局對(duì)象。我們知道ECMAScript其實(shí)是有一個(gè)全局對(duì)象的,這個(gè)全局對(duì)象在Node中是global;在瀏覽器中就是window對(duì)象;

    • 身份二:瀏覽器窗口對(duì)象。作為瀏覽器窗口時(shí),提供了對(duì)瀏覽器操作的相關(guān)的API;

Window全局對(duì)象

在瀏覽器中,window對(duì)象就是之前經(jīng)常提到的全局對(duì)象

  • 比如在全局通過var聲明的變量,會(huì)被添加到全局環(huán)境變量中,也就是會(huì)被添加到window上;
  • 比如window默認(rèn)給我們提供了全局的函數(shù)和類:setTimeout、Math、Date、Object等;
var message = 'hello'
function foo(){
    console.log("foo")
}
console.log(window.message)
window.foo()

 window.setTimeout(()=>{
     console.log('setTimout')
 })
 
 const obj = new window.Object()
 console.log(obj)

Window窗口對(duì)象

window是一個(gè)復(fù)雜的大對(duì)象,包含了大量的對(duì)象和方法

  • 第一:包含大量的屬性,localStorage、console、location、history、screenX、scrollX等等(大概60+個(gè)屬性);

  • 第二:包含大量的方法,alert、close、scrollTo、open等等(大概40+個(gè)方法);

  • 第三:包含大量的事件,focus、blur、load、hashchange等等(大概30+個(gè)事件);

  • 第四:包含從EventTarget繼承過來的方法,addEventListener、removeEventListener、dispatchEvent方法;

參考地址:MDN文檔:developer.mozilla.org/zh-CN/docs/…

[圖片上傳失敗...(image-bb8cd9-1676958099638)]

常見的屬性

// screenX和screenY屬性返回窗口相對(duì)于屏幕的X和Y坐標(biāo)。
console.log(window.screenX)
console.log(window.screenY)

// 監(jiān)聽滾動(dòng)
window.addEventListener("scroll", () => {
   console.log(window.scrollX, window.scrollY)
})
// 獲取窗口的寬度與高度:
console.log(window.outerHeight)
console.log(window.innerHeight)

常見的方法

const scrollBtn = document.querySelector("#scroll")
scrollBtn.onclick = function() {
    //1.scrollTo
    window.scrollTo({ top: 2000 })

    //2.close
    window.close()

    // 3.open
    window.open("http://www.baidu.com", "_self")
}

常見的事件

// 整個(gè)頁面以及所有資源加載完成
window.onload = function() {
  console.log("window窗口加載完畢~")
}

window.onfocus = function() {
  console.log("window窗口獲取焦點(diǎn)~")
}

window.onblur = function() {
  console.log("window窗口失去焦點(diǎn)~")
}

//hash 改變
const hashChangeBtn = document.querySelector("#hashchange")
hashChangeBtn.onclick = function() {
  location.hash = "aaaa"
}
window.onhashchange = function() {
  console.log("hash發(fā)生了改變")
}

Navigator 對(duì)象

Navigator 對(duì)象包含有關(guān)瀏覽器的信息。到底提供哪些信息很大程度取決于用戶的瀏覽器,但是也有一些公共的屬性,如userAgent 存在所有的瀏覽器中

Navigator 對(duì)象常見的屬性

window.navigator.appCodeName
//返回瀏覽器的代碼名   'Mozilla'
window.navigator.appName
//返回瀏覽器的名稱 'Netscape'
window.navigator.appVersion
//返回瀏覽器的平臺(tái)和版本信息
//'5.0 (iPhone; CPU iPhone OS 13_2_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/13.0.3 Mobile/15E148 Safari/604.1'
window.navigator.cookieEnabled
//返回指明瀏覽器中是否啟用 cookie 的布爾值 true
window.navigator.platform
//返回運(yùn)行瀏覽器的操作系統(tǒng)平臺(tái) 'MacIntel'
window.navigator.userAgent
//返回由客戶機(jī)發(fā)送服務(wù)器的user-agent 頭部的值
//'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.64 Safari/537.36'

使用場(chǎng)景

檢測(cè)插件

可以檢測(cè)瀏覽器內(nèi)是否安裝了特定的插件

navigator.plugins

判斷系統(tǒng)類型,版本信息

location對(duì)象

location 對(duì)象 是最有用的BOM對(duì)象之一

它既是window對(duì)象的屬性,也是document對(duì)象的屬性,window.location 和document.location引用的是同一個(gè)對(duì)象。

常見屬性

  • href: 當(dāng)前window對(duì)應(yīng)的超鏈接URL, 整個(gè)URL;
  • protocol: 當(dāng)前的協(xié)議;
  • host: 主機(jī)地址;
  • hostname: 主機(jī)地址(不帶端口);
  • port: 端口;
  • pathname: 路徑;
  • search: 查詢字符串;
  • hash: 哈希值;
  • username:URL中的username(很多瀏覽器已經(jīng)禁用);
  • password:URL中的password(很多瀏覽器已經(jīng)禁用);

常用方法

  • assign:賦值一個(gè)新的URL,并且跳轉(zhuǎn)到該URL中;
  • replace:打開一個(gè)新的URL,并且跳轉(zhuǎn)到該URL中(不同的是不會(huì)在瀏覽記錄中留下之前的記錄);
  • reload:重新加載頁面,可以傳入一個(gè)Boolean類型;如果把該方法的參數(shù)設(shè)置為 true,那么無論文檔的最后修改日期是什么,它都會(huì)繞過緩存,從服務(wù)器上重新下載該文檔。
location.assign("http://www.baidu.com")
location.

location.replace("http://www.baidu.com")
location.reload(false)

Screen對(duì)象

在編程中使用不多,只用來表明客戶端的能力,其中包括瀏覽器窗口外部的顯示器的信息,如像素的寬度和高度,每個(gè)瀏覽器的screen對(duì)象都包含著不同的屬性。根據(jù)具體業(yè)務(wù)場(chǎng)景去查詢就好。

history 對(duì)象

history對(duì)象允許我們?cè)L問瀏覽器曾經(jīng)的會(huì)話歷史記錄。

history對(duì)象有兩個(gè)屬性

  • length:會(huì)話中的記錄條數(shù);
  • state:當(dāng)前保留的狀態(tài)值;

history對(duì)象有五個(gè)方法:

  • back():返回上一頁,等價(jià)于history.go(-1);
  • forward():前進(jìn)下一頁,等價(jià)于history.go(1);
  • go():加載歷史中的某一頁;
  • pushState():打開一個(gè)指定的地址;
  • replaceState():打開一個(gè)新的地址,并且使用replace;

總結(jié)

BOM 以window對(duì)象為依托,表示瀏覽器窗口以及頁面可見區(qū)域

DOM

document Object Model 文檔對(duì)象模型

節(jié)點(diǎn)層次

DOM可以把任何HTML和XML文檔描繪成一個(gè)由多層節(jié)點(diǎn)構(gòu)成的樹形結(jié)構(gòu)。

<html>
  <head>
    <meta charset="UTF-8">
    <title>測(cè)試</title>
  </head>
  <body>
    <h1>標(biāo)題</h1>
    <ul>
      <1i>
        <a href="#">鏈接</a>
      </1i>
    </ul>
  </body>
</html>
image

上圖中就包括DOM的主要節(jié)點(diǎn)

Document文檔節(jié)點(diǎn)

表示整個(gè) HTML 頁面(相當(dāng)于 document 對(duì)象)

當(dāng)需要訪問任何標(biāo)簽、屬性或文本時(shí),都可以通過文檔節(jié)點(diǎn)進(jìn)行導(dǎo)航

Element元素節(jié)點(diǎn) ul h1 li

表示 HTML 頁面中的標(biāo)簽(即 HTML 頁面的結(jié)構(gòu))

當(dāng)訪問 DOM 樹時(shí),需要從查找元素節(jié)點(diǎn)開始

Attr 屬性節(jié)點(diǎn) href

表示 HTML 頁面中的開始標(biāo)簽包含的屬性

Text 文本節(jié)點(diǎn) 比如title的內(nèi)容


Node 類型

所有的DOM節(jié)點(diǎn)類型都繼承自Node接口,每個(gè)節(jié)點(diǎn)都有一個(gè)nodeType屬性,用于表明節(jié)點(diǎn)的類型。

對(duì)于HTML文檔,節(jié)點(diǎn)主要有以下六種類型:Document節(jié)點(diǎn)、DocumentType節(jié)點(diǎn)、Element節(jié)點(diǎn)、Attribute節(jié)點(diǎn)、Text節(jié)點(diǎn)和DocumentFragment節(jié)點(diǎn)。

分類

節(jié)點(diǎn) 名稱 含義
Document 文檔節(jié)點(diǎn) 整個(gè)文檔(window.document)
DocumentType 文檔類型節(jié)點(diǎn) 文檔的類型(比如 'DOCTYPE html')
Element 元素節(jié)點(diǎn) HTML元素(比如body、a等)
Attribute 屬性節(jié)點(diǎn) HTML元素的屬性(比如class="right")
Text 文本節(jié)點(diǎn) HTML文檔中出現(xiàn)的文本
DocumentFragment 文檔碎片節(jié)點(diǎn) 文檔的片段

常用屬性

Node 有幾個(gè)非常用且重要的屬性

  • nodeName:node節(jié)點(diǎn)的名稱;
  • nodeType:可以區(qū)分節(jié)點(diǎn)的類型;
  • nodeValue:node節(jié)點(diǎn)的值;
  • childNodes:所有的子節(jié)點(diǎn);

Document 類型

JavaScript 通過Document 類型表示整個(gè)文檔。

Document 類型可以表示 HTML 頁面或者 其他基于 XML 的文檔。不過最常用的應(yīng)用還是作為HTMLDocument實(shí)例的document 對(duì)象。

在瀏覽器中 document 對(duì)象是 HTMLDocument的一個(gè)實(shí)例,表示整個(gè)HTML頁面。而且,document對(duì)象也是window對(duì)象的一個(gè)屬性,因此可以作為全局對(duì)象來訪問。

常見屬性

  • document.body 返回文檔的body元素
  • document.title 返回當(dāng)前文檔的標(biāo)題
  • document.head 返回當(dāng)前文檔的head內(nèi)容
  • document.children[0] 返回當(dāng)前文檔html內(nèi)容

常見方法

  • 創(chuàng)建元素
  • 獲取標(biāo)簽元素
// 創(chuàng)建元素
const imageEl = document.createElement("img")
const imageEl2 = new HTMLImageElement()

// 獲取元素
const divEl1 = document.getElementById("box")
const divEl2 = document.getElementsByTagName("div")
const divEl3 = document.getElementsByName("title")
  • 文檔寫入

將輸出流寫入到網(wǎng)頁的能力。

document.write()
document.writeln()
document.open()
document.close()

Element 類型

Element 類型用于表現(xiàn)XML和HTML元素,提供了對(duì)元素標(biāo)簽名,子節(jié)點(diǎn)及特性的訪問

我們平時(shí)創(chuàng)建的div、p、span等元素在DOM中表示為Element元素

常見屬性

  • 子元素 children childNodes
  • tagName
  • id/class
  • clientWidth/clientHeight
  • clientLeft/clientTop
  • offsetLeft/offsetTop

常見方法

獲取特性 getAttribute 修改某個(gè)特性 setAttribute

創(chuàng)建元素 document.createElement()

Text 類型

文本節(jié)點(diǎn)由Text類型表示,包含的是可以照字面解釋的純文本內(nèi)容

  • 創(chuàng)建文本節(jié)點(diǎn):document.createTextNode()

Comment 類型

注釋

DocumentFragment 類型

DOM規(guī)定文檔片段是一種輕量級(jí)的文檔,不會(huì)像完整的文檔那樣占有額外的資源

可以在里面保存將來會(huì)添加到文檔的節(jié)點(diǎn)。要?jiǎng)?chuàng)建文檔片段,可以使用document.createDocumentFragment()

文檔片段本身永遠(yuǎn)不會(huì)成為文檔樹的一部分

Attr類型

元素的特性在DOM中用Attr類型來表示。很少使用。

總結(jié)

DOM1級(jí)將HTML和XML文檔看作一個(gè)層次化的節(jié)點(diǎn)樹,方便js來直接操作。

DOM 是由各種節(jié)點(diǎn)構(gòu)成的

  • 最基本節(jié)點(diǎn)是Node,所有的節(jié)點(diǎn)都繼承自Node
  • Document 表示整個(gè)文檔
  • Element 表示文檔中的HTML 或者XML 元素

DOM擴(kuò)展

對(duì)DOM的擴(kuò)展主要包括 選擇符API 和 HTML5

選擇符API

選擇符,最核心的兩個(gè)API 就是 querySelector() 和 querySelectorAll()

可以通過Document 和 Element 類型的實(shí)例調(diào)用

const divEl1 = document.querySelector(".content")
const divEl2 = document.querySelectorAll(".content")

HTML5

html5涉及的面非常廣,我們這里這討論與DOM 節(jié)點(diǎn)相關(guān)的內(nèi)容

獲取dom元素

  • getElementsByClassName() 可以通過document 對(duì)象和所有HTML元素調(diào)用該方法,查詢一個(gè)或者包含類名的字符串

自定義數(shù)據(jù)屬性

可以為元素添加非標(biāo)準(zhǔn)的屬性,但是要添加前綴 data-

通過元素的 dataset 屬性來訪問自定義的屬性的值

<div id ="myDiv" data-appId = "12345" data-name="yz"></div>

var div = document.getElementById('myDiv')

var appId = div.dataset.appId
var name = div.dataset.name

插入標(biāo)記

innerHTML屬性

  • 可以獲取調(diào)用元素的所有子節(jié)點(diǎn)對(duì)應(yīng)的html片段
  • 可以是根據(jù)指定的值創(chuàng)建DOM樹,替換原有的元素節(jié)點(diǎn)

一般我們插入大量新HTML標(biāo)記時(shí),使用innerHTML 與通過多次DOM操作先創(chuàng)建節(jié)點(diǎn)再指定它們之間的關(guān)系相比,效率更高,因?yàn)樵谠O(shè)置innerHTML 時(shí)就會(huì)先創(chuàng)建一個(gè)HTML解析器,這個(gè)解析是在瀏覽器級(jí)別的基礎(chǔ)上代碼(c++)運(yùn)行的,因此比執(zhí)行js 快得多,當(dāng)然也會(huì)帶來 創(chuàng)建銷毀 html 解析器也有性能消耗。所以控制次數(shù)。

DOM2和DOM3

DOM1級(jí)主要定義的是HTML和XML文檔的底層結(jié)構(gòu)

DOM2級(jí)和DOM3級(jí)是在這個(gè)結(jié)構(gòu)基礎(chǔ)上引入了更多的交互能力和特性。擴(kuò)展了DOM API,以滿足操作DOM的所有需求,同時(shí)提供更好的錯(cuò)誤處理和特性監(jiān)測(cè)能力

DOM變化

  • Node 類型 Document 類型 Element 類型 增加了命名空間的概念
  • DOM3引入 兩個(gè)輔助比較節(jié)點(diǎn)的方法 isSameNode() 和 isEqualNode( )
-   一個(gè)判斷相同 兩個(gè)節(jié)點(diǎn)引用的是同一個(gè)對(duì)象
-   一個(gè)判斷相等 兩個(gè)節(jié)點(diǎn)是同類型的對(duì)象,具有相等的屬性(nodeName,nodeValue等等),而且他們的attributes和 childNodes屬性也相等(相同位置包含相同的)
var div1 = document.createElement("div")
div1.setAttribute("class","box")

var div1 = document.createElement("div")
div1.setAttribute("class","box")

alert(div1.isSameNode(div1)) // true
alert(div1.isEqualNode(div1)) // true
alert(div1.isSameNode(div2)) // false
  • iframe中的文檔對(duì)象可以通過contentDocument 訪問 所有瀏覽器都支持
var iframe = document.getElementById("myIframe")
var iframe = iframe.contentDocuemnt || iframe.contentWindow.document

樣式變化

訪問元素的樣式

  • 新增style屬性 可以通過style對(duì)象訪問所有樣式信息,也可以進(jìn)行修改
  • document.default.getComputedStyle(dom) 可以返回整個(gè)dom的樣式

操作樣式表

  • 大多數(shù)情況下,使用style屬性就可以滿足所有操作樣式柜子的需求

元素大小

  • 偏移量
    • offsetHeight offsetWdith offsetLeft offsetTop
    • 可以通過以上四個(gè)屬性計(jì)算
  • 客戶區(qū)大小
    • clientHeight clientWidth
  • 滾動(dòng)大小
    • scrollHeight scrollWidth scrollLeft scrollTop
    • scrollLeft scrollTop可以用來確定當(dāng)前元素滾動(dòng)狀態(tài)
image
  • 確定元素大小

    • getBoundClientRect()返回一個(gè)矩形對(duì)象,包含四個(gè)屬性 left top right bottom,給出了元素在頁面中相對(duì)于視口的位置。
    • top/y:元素上邊到視窗上邊的距離;
    • right:元素右邊到視窗左邊的距離;
    • bottom:元素下邊到視窗上邊的距離;
    • left/x:元素左邊到視窗左邊的距離;
    • width:元素的寬度;
    • height:元素的高度;
image

范圍

為了讓開發(fā)人員更方便得控制頁面,dom2 定義了范圍 range接口。

document.createRange()

  • 用dom范圍實(shí)現(xiàn)簡(jiǎn)單選擇
var range1 = document.createRange()
var p1 = document.getElementById('p1')

range1.selcetNode(p1)
  • 也可以操作修改dom范圍中的內(nèi)容

小結(jié)

dom2 級(jí)主要新增了操作樣式的能力和操作范圍內(nèi)dom的能力

總結(jié)

對(duì)于dom和bom的學(xué)習(xí),我所秉持的觀點(diǎn)依然是,抓大放小,建立知識(shí)體系,常用的api可以了解,不常用的api知道去哪里查就好,因?yàn)槠匠I(yè)務(wù)開發(fā)也接觸不到太底層的代碼。

框架發(fā)展到今天,對(duì)我們前端開發(fā)來說,已經(jīng)很少去操作Dom了,但框架已經(jīng)幫助我們做了,對(duì)于Bom的交互也有很多封裝成熟的函數(shù)庫,但是如果要對(duì)前端深入學(xué)習(xí),我覺得這些知識(shí)還是必須掌握的,前端學(xué)習(xí)js 永遠(yuǎn)是基礎(chǔ),框架函數(shù)庫,都是錦上添花的東西。

我們要知其然,更要知其所以然。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容