<kbd id="5sdj3"></kbd>
<th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>

    瀏覽器的DOM和BOM

    共 14815字,需瀏覽 30分鐘

     ·

    2024-05-29 08:31


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

    我們可以將BOM看成是連接JavaScript腳本與瀏覽器窗口的橋梁。

    BOM主要包括一下的對(duì)象模型:

    • window:包括全局屬性、方法,控制瀏覽器窗口相關(guān)的屬性、方法;
    • location:瀏覽器連接到的對(duì)象的位置(URL);
    • history:操作瀏覽器的歷史;
    • document:當(dāng)前窗口操作文檔的對(duì)象;

    一. window對(duì)象

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

    • 身份一:全局對(duì)象。
      • 我們知道ECMAScript其實(shí)是有一個(gè)全局對(duì)象的,這個(gè)全局對(duì)象在Node中是global;
      • 在瀏覽器中就是window對(duì)象;
    • 身份二:瀏覽器窗口對(duì)象。
      • 作為瀏覽器窗口時(shí),提供了對(duì)瀏覽器操作的相關(guān)的API;

    1.1. global全局對(duì)象

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

    • 比如在全局通過var聲明的變量,會(huì)被添加到GO中,也就是會(huì)被添加到window上;
    • 比如window默認(rèn)給我們提供了全局的函數(shù)和類:setTimeout、Math、Date、Object等;

    通過var聲明的變量:

    var message = "Hello World"
    function foo({
      console.log("foo function")
    }

    console.log(window.message)
    window.foo()

    全局提供的類和方法:

    window.setTimeout(() => {
      console.log("setTimeout")
    }, 1000)

    const obj = new window.Object()
    console.log(obj)

    const date = new window.Date()
    console.log(date)

    這些用法是我們之前講過的,并且也是作為JavaScript語言本身所擁有的一些特性。

    那么接下來我們來看一下作為窗口對(duì)象,它擁有哪些特性。

    1.2. window窗口對(duì)象

    事實(shí)上window對(duì)象上肩負(fù)的重?fù)?dān)是非常大的:

    • 第一:包含大量的屬性,localStorage、console、location、history、screenX、scrollX等等(大概60+個(gè)屬性);
    • 第二:包含大量的方法,alert、close、scrollTo、open等等(大概40+個(gè)方法);
    • 第三:包含大量的事件,focus、blur、load、hashchange等等(大概30+個(gè)事件);
    • 第四:包含從EventTarget繼承過來的方法,addEventListener、removeEventListener、dispatchEventListener方法;

    那么這些大量的屬性、方法、事件在哪里查看呢?

    • MDN文檔:https://developer.mozilla.org/zh-CN/docs/Web/API/Window

    查看MDN文檔時(shí),我們會(huì)發(fā)現(xiàn)有很多不同的符號(hào),這里我解釋一下是什么意思:

    • 刪除符號(hào):表示這個(gè)API已經(jīng)廢棄,不推薦繼續(xù)使用了。
    刪除符號(hào)
    • 點(diǎn)踩符號(hào):表示這個(gè)API不屬于W3C規(guī)范,某些瀏覽器有實(shí)現(xiàn)(所以兼容性的問題)
    點(diǎn)踩符號(hào)
    • 實(shí)驗(yàn)符號(hào):該API是實(shí)驗(yàn)性特性,以后可能會(huì)修改,并且存在兼容性問題。

      實(shí)驗(yàn)符號(hào)

    1.2.1. 常見的屬性

    // 瀏覽器高度
    console.log(window.outerHeight)
    console.log(window.innerHeight)

    console.log("screenX:"window.screenX)
    console.log("screenY:"window.screenY)

    // 監(jiān)聽
    window.addEventListener("scroll", (event) => {
      console.log(window.scrollY)
      console.log(window.scrollX)
    })

    1.2.2. 常見的方法

    // alert("Hello World")

    // close方法
    const closeBtn = document.querySelector("#close")
    closeBtn.onclick = function({
      close()
    }

    // moveTo
    const scrollBtn = document.querySelector("#scroll")
    scrollBtn.onclick = function({
      scrollTo({ top1000 })
    }

    // 打開新創(chuàng)建
    const openBtn = document.querySelector("#open")
    openBtn.onclick = function({
      open("./about.html""_self")
    }

    1.2.3. 常見的事件

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

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

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

    // hash改變
    const hashBtn = document.querySelector("#hash")
    hashBtn.onclick = function({
      location.hash = "aaa"
    }
    window.onhashchange = function({
      console.log("hash被修改了")
    }

    1.2.4. EventTarget

    Window繼承自EventTarget,所以會(huì)繼承其中的屬性和方法:

    • addEventListener:注冊(cè)某個(gè)事件類型以及事件處理函數(shù);
    • removeEventListener:移除某個(gè)事件類型以及事件處理函數(shù);
    • dispatchEvent:派發(fā)某個(gè)事件類型到EventTarget上;
    const scrollHandler = () => {
      console.log("window發(fā)生了滾動(dòng)~")
    }
    const clickHandler = () => {
      console.log("window發(fā)生了點(diǎn)擊~")
    }

    window.addEventListener("scroll", scrollHandler)
    window.addEventListener("click", clickHandler)

    const removeBtn = document.querySelector("#removeEvent")
    removeBtn.onclick = function({
      console.log("-----")
      window.removeEventListener("click", clickHandler)
      window.removeEventListener("scroll", scrollHandler)
    }

    自己來派發(fā)事件:

    const dispatchBtn = document.querySelector("#dispatch")

    dispatchBtn.onclick = function({
      window.dispatchEvent(new Event("coderwhy"))
    }

    window.addEventListener("coderwhy", () => {
      console.log("監(jiān)聽到了coderwhy事件")
    })

    默認(rèn)事件監(jiān)聽:

    • https://developer.mozilla.org/zh-CN/docs/Web/Events

    1.3. location位置

    1.3.1. 常見的屬性

    比如我們有一個(gè)地址:

    • http://coderwhy:[email protected]:5500/27_%E6%B5%8F%E8%A7%88%E5%99%A8%E5%AF%B9%E8%B1%A1/index.html?name=why&age=18#abc
    // Location類型的對(duì)象
    console.log(window.location)

    // href: 當(dāng)前window對(duì)應(yīng)的超鏈接URL, 整個(gè)URL
    console.log(location.href)

    // protocol: 當(dāng)前的協(xié)議
    console.log(location.protocol)

    // host: 主機(jī)地址
    console.log(location.host)

    // hostname: 主機(jī)地址(不帶端口)
    console.log(location.hostname)

    // port: 端口
    console.log(location.port)

    // pathname: 路徑
    console.log(location.pathname)

    // search: 查詢字符串
    console.log(location.search)

    // hash: 
    console.log(location.hash)

    // username:
    console.log(location.username)

    // password
    console.log(location.password)

    我們會(huì)發(fā)現(xiàn)location其實(shí)是URL的一個(gè)抽象實(shí)現(xiàn):

    URL解析

    1.3.2. 常見的操作

    location有如下常用的方法:

    • assign:賦值一個(gè)新的URL,并且跳轉(zhuǎn)到該URL中;
    • replace:打開一個(gè)新的URL,并且跳轉(zhuǎn)到該URL中(不同的是不會(huì)在瀏覽記錄中留下之前的記錄);
    • reload:重新加載頁面,可以傳入一個(gè)Boolean類型;
    const locationBtn = document.querySelector("#location")
    locationBtn.onclick = function({
      // location.assign("http://www.baidu.com")
      // location.replace("http://www.baidu.com")
      location.reload()
    }

    另外我們修改location的很多屬性,也會(huì)造成瀏覽器重新加載地址:

    // location.href = "http://www.baidu.com"
    // location.host = "www.baidu.com:80"

    1.4. history屬性

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

    有兩個(gè)屬性:

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

    有五個(gè)方法:

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

    const jumpBtn = document.querySelector("#jump")
    const backBtn = document.querySelector("#back")

    jumpBtn.onclick = function({
      history.pushState({name"why"}, "11""aaa")
      console.log(history.length, history.state)
    }

    backBtn.onclick = function({
      history.back()
      console.log(history.length, history.state)
    }

    二. document對(duì)象

    2.1. 整體架構(gòu)

    瀏覽器是用來展示網(wǎng)頁的,而網(wǎng)頁中最重要的就是里面各種的標(biāo)簽元素,JavaScript很多時(shí)候是需要操作這些元素的。

    • JavaScript如何操作元素呢?通過Document Object Model(DOM,文檔對(duì)象模型)。
    • DOM給我們提供了一系列的模型和對(duì)象,讓我們可以方便的來操作Web頁面。
    DOM模型關(guān)系圖

    當(dāng)我們有一個(gè)頁面時(shí),這個(gè)頁面就可以用上面的模式來表示出來:

    <!DOCTYPE html>
    <html lang="en">
    <head>
      <title>Document</title>
    </head>
    <body>
      <div>
        <!-- 我是注釋 -->
        <h2>哈哈哈</h2>
        <strong>呵呵呵</strong>
      </div>
    </body>
    </html>

    這個(gè)網(wǎng)頁會(huì)形式一個(gè)對(duì)象樹,這些對(duì)象都是我們上面的模型所創(chuàng)建出來的:

    • 比如整個(gè)的頁面是HTMLDocument對(duì)象;
    • 比如body、div、h2等都是HTMLElement對(duì)象;
    • 比如哈哈哈、呵呵呵文本都是Text對(duì)象;
    • 比如我是注釋都是Comment對(duì)象;
    • 比如其中的屬性是Attr對(duì)象;

    2.2. EventTarget

    document.addEventListener("click", () => {
      console.log("ducument被點(diǎn)擊")
    })


    const boxDiv = document.querySelector("#box")
    boxDiv.addEventListener("click", () => {
      console.log("box被點(diǎn)擊")
    })

    2.3. Node節(jié)點(diǎn)

    所有的DOM節(jié)點(diǎn)類型都繼承自Node接口。

    • https://developer.mozilla.org/zh-CN/docs/Web/API/Node

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

    nodeName:node節(jié)點(diǎn)的名稱。

    image-20211112164535329

    nodeType:可以區(qū)分節(jié)點(diǎn)的類型。

    image-20211112164303286

    childNodes

    firstChild

    2.4. document

    // title
    document.title = "Coderwhy"

    // body/head
    console.log(document.body)
    console.log(document.head)

    // children
    console.log(document.children)

    // location
    console.log(document.location)
    console.log(window.location === document.location)

    // 方法
    // 1.創(chuàng)建和添加createElement
    const h2El = document.createElement("h2")
    h2El.textContent = "Hello World"
    document.body.appendChild(h2El)

    // 2.刪除元素
    setTimeout(() => {
      document.body.removeChild(h2El)
    }, 2000);

    // 3.獲取元素
    const el1 = document.getElementsByName("abc")
    const el2 = document.getElementsByTagName("div")
    console.log(el1, el2)

    const el3 = document.querySelector("div")
    const el4 = document.querySelectorAll("div")
    console.log(el3, el4)

    2.5. element

    const boxDiv = document.querySelector("#box")

    // 1.獲取子元素
    console.log(boxDiv.children)
    console.log(boxDiv.childNodes)

    // 2.tagName
    console.log(boxDiv.tagName)

    // 3.id/class
    console.log(boxDiv.id)
    console.log(boxDiv.className)
    console.log(boxDiv.classList)

    // 4.clientWidth/clientHeight/clientLeft/clientTop
    console.log(boxDiv.clientWidth, boxDiv.clientHeight)
    // 邊框?qū)挾群透叨?/span>
    console.log(boxDiv.clientLeft, boxDiv.clientTop)
    // offsetWidth/offsetHeight
    console.log(boxDiv.offsetLeft, boxDiv.offsetTop)

    // 方法(操作屬性)
    const attr1 = boxDiv.getAttribute("name")
    console.log(attr1)

    boxDiv.setAttribute("height""1.88")

    三. 事件處理

    3.1. 事件監(jiān)聽

    前面我們講到了JavaScript腳本和瀏覽器之間交互時(shí),瀏覽器給我們提供的BOM、DOM等一些對(duì)象模型。

    事實(shí)上還有一種需要和瀏覽器經(jīng)常交互的事情就是事件監(jiān)聽:

    • 瀏覽器在某個(gè)時(shí)刻可能會(huì)發(fā)生一些事件,比如鼠標(biāo)點(diǎn)擊、移動(dòng)、滾動(dòng)、獲取、失去焦點(diǎn)、輸入內(nèi)容等等一系列的事件;
    • 我們需要以某種方式(代碼)來對(duì)其進(jìn)行響應(yīng),進(jìn)行一些事件的處理;

    在Web當(dāng)中,事件在瀏覽器窗口中被觸發(fā),并且通過綁定到某些元素上或者瀏覽器窗口本身,那么我們就可以給這些元素或者window窗口來綁定事件的處理程序,來對(duì)事件進(jìn)行監(jiān)聽。

    事件監(jiān)聽方式一:

      <button onclick="console.log('按鈕1被點(diǎn)擊了')">按鈕1</button>
      <button onclick="btnClick()">按鈕2</button>

      <script>
        function btnClick({
          console.log("按鈕2被點(diǎn)擊了")
        }
      
    </script>

    事件監(jiān)聽方式二:

    const btn3 = document.querySelector("#btn3")
    btn3.onclick = function({
      console.log("按鈕3被點(diǎn)擊了")
    }

    事件監(jiān)聽方式三:

    btn3.addEventListener("click", () => {
      console.log("按鈕3被點(diǎn)擊了")
    })

    3.2. 事件流

    事實(shí)上對(duì)于事件有一個(gè)概念叫做事件流,為什么會(huì)產(chǎn)生事件流呢?

    • 我們可以想到一個(gè)問題:當(dāng)我們?cè)跒g覽器上對(duì)著一個(gè)元素點(diǎn)擊時(shí),你點(diǎn)擊的不僅僅是這個(gè)元素本身;
    • 這是因?yàn)槲覀兊腍TML元素是存在父子元素疊加層級(jí)的;
    • 比如一個(gè)span元素是放在div元素上的,div元素是放在body元素上的,body元素是放在html元素上的;
    <body>
      <div class="box">
        <span class="span">我是span元素</span>
      </div>
    </body>

    我們可以監(jiān)聽這些元素的點(diǎn)擊:

    document.body.addEventListener("click", () => {
      console.log("body被點(diǎn)擊")
    })

    const divEl = document.querySelector(".box")
    const spanEl = document.querySelector(".span")

    divEl.addEventListener("click", () => {
      console.log("div被點(diǎn)擊")
    })

    spanEl.addEventListener("click", () => {
      console.log("span被點(diǎn)擊")
    })

    我們會(huì)發(fā)現(xiàn)默認(rèn)情況下事件是從最內(nèi)層的span向外依次傳遞的順序,這個(gè)順序我們稱之為事件冒泡(Event Bubble)。

    • 事實(shí)上,還有另外一種監(jiān)聽事件流的方式就是從外層到內(nèi)層(body -> span),這種稱之為事件捕獲(Event Capture);
    • 為什么會(huì)產(chǎn)生兩種不同的處理流呢?
      • 這是因?yàn)樵缙跒g覽器開發(fā)時(shí),不管是IE還是Netscape公司都發(fā)現(xiàn)了這個(gè)問題,但是他們采用了完全相反的事件流來對(duì)事件進(jìn)行了傳遞;
      • IE采用了事件冒泡的方式,Netscape采用了事件捕獲的方式;

    那么我們?nèi)绾稳ケO(jiān)聽事件捕獲的過程呢?

    // 事件捕獲的監(jiān)聽
    document.body.addEventListener("click", () => {
      console.log("body被點(diǎn)擊")
    }, true)

    divEl.addEventListener("click", () => {
      console.log("div被點(diǎn)擊")
    }, true)

    spanEl.addEventListener("click", () => {
      console.log("span被點(diǎn)擊")
    }, true)

    并且會(huì)發(fā)現(xiàn),如果我們同時(shí)有事件冒泡和時(shí)間捕獲的監(jiān)聽,那么會(huì)優(yōu)先監(jiān)聽到事件捕獲的:

    事件捕獲階段: body被點(diǎn)擊
    事件捕獲階段: div被點(diǎn)擊
    事件捕獲階段: span被點(diǎn)擊
    事件冒泡階段: span被點(diǎn)擊
    事件冒泡階段: div被點(diǎn)擊
    事件冒泡階段: body被點(diǎn)擊
    image-20211115110034117

    3.3. 事件對(duì)象

    當(dāng)一個(gè)事件發(fā)生時(shí),就會(huì)有和這個(gè)事件相關(guān)的很多信息:

    • 比如事件的類型是什么,你點(diǎn)擊的是哪一個(gè)元素,點(diǎn)擊的位置是哪里等等相關(guān)的信息;
    • 那么這些信息會(huì)被封裝到一個(gè)Event對(duì)象中;
    • 該對(duì)象給我們提供了想要的一些屬性,以及可以通過該對(duì)象進(jìn)行某些操作;

    常見的屬性:

    • type:事件的類型;
    • target:當(dāng)前事件發(fā)生的元素;
    • currentTarget:當(dāng)前處理事件的元素;
    • offsetX、offsetY:點(diǎn)擊元素的位置;
    spanEl.addEventListener("click", (event) => {
      console.log("事件冒泡階段: span被點(diǎn)擊:", event)
      console.log("事件的類型:", event.type)
      console.log("點(diǎn)擊的元素:", event.target, event.currentTarget)
      console.log("點(diǎn)擊的位置:", event.offsetX, event.offsetY)
      console.log("點(diǎn)擊的數(shù)據(jù):", event.target.dataset)
    })

    常見的方法:

    • preventDefault:取消事件的默認(rèn)行為;
    • stopPropagation:阻止事件的進(jìn)一步傳遞;
    // 阻止a元素的默認(rèn)行為
    const aEl = document.querySelector("a")
    aEl.addEventListener("click", (event) => {
      event.preventDefault()
      window.open(aEl.href)
    })

    // 事件捕獲的監(jiān)聽
    document.body.addEventListener("click", (event) => {
      console.log("事件捕獲階段: body被點(diǎn)擊")
    }, true)

    divEl.addEventListener("click", (event) => {
      console.log("事件捕獲階段: div被點(diǎn)擊")
      event.stopPropagation()
    }, true)

    spanEl.addEventListener("click", () => {
      console.log("事件捕獲階段: span被點(diǎn)擊")
    }, true)

    事件類型:https://developer.mozilla.org/zh-CN/docs/Web/Events


    瀏覽 67
    點(diǎn)贊
    評(píng)論
    收藏
    分享

    手機(jī)掃一掃分享

    分享
    舉報(bào)
    評(píng)論
    圖片
    表情
    推薦
    點(diǎn)贊
    評(píng)論
    收藏
    分享

    手機(jī)掃一掃分享

    分享
    舉報(bào)

    <kbd id="5sdj3"></kbd>
    <th id="5sdj3"></th>

  • <dd id="5sdj3"><form id="5sdj3"></form></dd>
    <td id="5sdj3"><form id="5sdj3"><big id="5sdj3"></big></form></td><del id="5sdj3"></del>

  • <dd id="5sdj3"></dd>
    <dfn id="5sdj3"></dfn>
  • <th id="5sdj3"></th>
    <tfoot id="5sdj3"><menuitem id="5sdj3"></menuitem></tfoot>

  • <td id="5sdj3"><form id="5sdj3"><menu id="5sdj3"></menu></form></td>
  • <kbd id="5sdj3"><form id="5sdj3"></form></kbd>
    AAAAAAA级片 | 爽灬爽灬无码无遮挡在线看 | 日日撸日日操 | 极度兴感美女黄色操逼视频 | 美女被操网站在线观看 |