document 和 window 的區別?#
就是簡而言之,dom 是一個對象,win 也是一個對象。他們都是 Web API 裡的一個不同角色。
window 對象#
- window 對象
window 就是窗口嗎,它代表瀏覽器的一個窗口或標籤頁,並且是 JavaScript 中的全局對象。它提供了許多控制瀏覽器窗口的方法和屬性。
作用域:window 是最頂層的對象,在瀏覽器中的任何 JavaScript 代碼中都可以直接訪問 window 及其屬性和方法,無需任何限定前綴。
功能:window 對象包含了瀏覽器窗口的屬性,如窗口的大小、位置等,同時也提供了一些方法來控制瀏覽器窗口的行為,如打開新窗口、定時器函數(setTimeout、setInterval)、瀏覽器歷史控制等。此外,它還是所有全局變量和全局函數的宿主。
document 對象#
- document 對象
定義:document 對象是 window 對象的一個屬性,它代表了加載在窗口中的 HTML 文檔,是 Document Object Model(DOM)的入口點。
作用域:document 對象專門用於操作和訪問文檔的內容,比如 HTML 元素、CSS 樣式等。
功能:document 對象提供了許多方法來訪問和修改文檔內容,如獲取和設置元素的內容、創建新的 HTML 元素、查詢選擇器等。通過 document 對象,可以實現對頁面內容的動態修改和交互。
總的來說,window 對象代表了瀏覽器窗口本身,是所有全局 JavaScript 對象、函數和變量的父級對象,而 document 對象代表了窗口中加載的文檔,是所有 HTML 文檔元素的容器。在實際開發中,根據需要操作的是瀏覽器窗口本身還是窗口中的文檔內容,來決定使用 window 還是 document。
就簡單而言,dom 對象操控的是文檔的內容,而 window 對象操控的是瀏覽器窗口的屬性和方法。所以,如果你想開定時器,監控窗口的變化你需要用到的是 windows 對象,如果你想要改變文檔的內容那你則需要使用 document 對象。
所以當時其實甘特圖最主要的問題是,我不知道 windows API 和 document API 的區別。我不懂原生的 html,當這塊知識缺失了自然是不會去設計這個邏輯的。
所以本質上就是會用封裝好的 API。
Dom 流 API 學習#
如何獲取元素#
在 DOM 中獲取元素主要涉及以下幾個 API:
-
getElementById()
- 通過元素的 ID 獲取單個元素。ID 在一個 HTML 文檔中應該是唯一的。
let element = document.getElementById('elementId'); // 通過ID獲取元素
-
getElementsByClassName()
- 通過元素的類名獲取一個元素集合。這個方法返回的是一個類數組對象,包含所有匹配指定類名的元素。
let elements = document.getElementsByClassName('className'); // 通過類名獲取元素集合
-
getElementsByTagName()
- 通過元素的標籤名(如
div
,p
,a
等)獲取元素集合。同樣返回一個類數組對象,包含所有匹配指定標籤名的元素。
let elements = document.getElementsByTagName('tagName'); // 通過標籤名獲取元素集合
- 通過元素的標籤名(如
-
querySelector()
- 使用 CSS 選擇器獲取第一個匹配的元素。這是一個非常強大的方法,可以使用複雜的 CSS 選擇器來定位元素。
let element = document.querySelector('.className'); // 通過CSS選擇器獲取第一個匹配的元素
-
querySelectorAll()
- 使用 CSS 選擇器獲取所有匹配的元素集合。這個方法返回的是一個
NodeList
對象,包含所有匹配指定 CSS 選擇器的元素。
let elements = document.querySelectorAll('.className'); // 通過CSS選擇器獲取所有匹配的元素
- 使用 CSS 選擇器獲取所有匹配的元素集合。這個方法返回的是一個
-
closest()
- 從元素本身開始,在其祖先元素中(包括自己),找到第一個匹配給定 CSS 選擇器的元素。這個方法對於尋找元素的最近父級匹配非常有用。自下向上尋找。
let closestElement = element.closest('.className'); // 在元素的祖先中找到最近的匹配給定選擇器的元素
-
children
- 獲取一個元素的子元素集合,不包括文本和註釋節點。這是一個元素對象的屬性,返回直接子元素的 HTML 集合。
let childElements = parentElement.children; // 獲取parentElement的所有直接子元素
-
childNodes
- 獲取一個元素的所有子節點,包括元素節點、文本節點和註釋節點。這同樣是元素對象的一個屬性,返回一個節點列表。
let childNodes = parentElement.childNodes; // 獲取parentElement的所有子節點,包括文本和註釋
-
parentElement
和parentNode
- 通過一個元素獲取其父元素。
parentElement
返回父級元素節點,而parentNode
可以返回任何類型的父節點,包括文本節點和註釋節點。
let parent = childElement.parentElement; // 獲取childElement的父元素 let parentNode = childElement.parentNode; // 獲取childElement的父節點
- 通過一個元素獲取其父元素。
-
nextSibling
和previousSibling
- 分別獲取元素的下一個和上一個同級節點。這兩個屬性返回的可以是任何類型的節點,包括元素節點、文本節點和註釋節點。
let nextNode = element.nextSibling; // 獲取element的下一個同級節點 let prevNode = element.previousSibling; // 獲取element的上一個同級節點
-
nextElementSibling
和previousElementSibling
- 類似於
nextSibling
和previousSibling
,但這兩個屬性只返回元素節點,忽略文本節點和註釋節點。
let nextElement = element.nextElementSibling; // 獲取element的下一個同級元素 let prevElement = element.previousElementSibling; // 獲取element的上一個同級元素
- 類似於
-
firstChild
和lastChild
- 分別獲取元素的第一個和最後一個子節點。這些子節點可以是任何類型的節點,包括元素節點、文本節點和註釋節點。
let first = parentElement.firstChild; // 獲取parentElement的第一個子節點 let last = parentElement.lastChild; // 獲取parentElement的最後一個子節點
-
firstElementChild
和lastElementChild
- 類似於
firstChild
和lastChild
,但這兩個屬性只返回元素節點,忽略文本節點和註釋節點。
let firstElement = parentElement.firstElementChild; // 獲取parentElement的第一個子元素 let lastElement = parentElement.lastElementChild; // 獲取parentElement的最後一個子元素
- 類似於
通過綜合運用這些 API,可以有效地遍歷和操作 DOM 樹,實現對頁面結構的靈活控制。
通過這些 API,可以根據不同的條件和需求,在 DOM 樹中靈活地獲取單個或多個元素。
如何創建元素#
創建元素#
document.createElement('tag')
創建文本節點#
document.createTextNode('text')
什麼是文本節點?
文本節點就是 html 中的文本,比如 p 標籤中的文本,a 標籤中的文本等等。
createTextNode 怎麼使用?
var text = document.createTextNode('text')
這是什麼效果?
<p>text</p>
text 在這裡面是它文字的內容。
如何添加元素#
添加元素#
parent.appendChild(child)
什麼效果?
<div>
<p>text</p>
</div>
插入元素#
parent.insertBefore(newNode, referenceNode)
舉個例子
var parent = document.getElementById('parent')
var newNode = document.createElement('p')
var referenceNode = document.getElementById('reference')
parent.insertBefore(newNode, referenceNode)
這個效果是什麼?
<div id="parent">
<p>newNode</p>
<p id="reference">referenceNode</p>
</div>
referenceNode 起了什麼作用?
referenceNode 是一個參考節點,它的作用是在參考節點之前插入新的節點。
如何刪除元素#
刪除元素#
parent.removeChild(child)
舉個例子
var parent = document.getElementById('parent')
var child = document.getElementById('child')
parent.removeChild(child)
這個效果是什麼?給出對比
<div id="parent">
<p id="child">child</p>
</div>
<div id="parent">
</div>
如何修改元素內容?#
在 DOM(文檔對象模型)中修改元素內容主要涉及以下幾個常用的 API:
-
innerText
和textContent
這兩個屬性都可以用來獲取或設置一個元素的文本內容。innerText
反映了元素及其子元素的 “渲染” 文本內容,即按照樣式顯示在頁面上的內容,而textContent
則獲取或設置元素內所有子節點的內容,包括<script>
和<style>
標籤內的文本。element.innerText = '新的文本內容'; // 設置元素的可見文本 let content = element.innerText; // 獲取元素的可見文本 element.textContent = '新的全部文本內容'; // 設置元素的全部文本,包含所有子節點 let fullContent = element.textContent; // 獲取元素的全部文本,包含所有子節點
-
innerHTML
innerHTML
屬性可以用來獲取或設置元素內的 HTML 內容。與innerText
和textContent
不同,innerHTML
會包含所有的 HTML 標籤。element.innerHTML = '<strong>加粗的文本</strong>'; // 設置元素內的HTML內容 let htmlContent = element.innerHTML; // 獲取元素內的HTML內容
-
outerHTML
outerHTML
屬性可以獲取或設置包含元素本身及其所有子節點的 HTML。element.outerHTML = '<div><strong>新元素及其內容</strong></div>'; // 替換元素及其內容
-
createElement
和appendChild
/replaceChild
這些方法用於創建新的 DOM 元素並將其插入到文檔中。createElement
用於創建一個新的元素節點,appendChild
用於將創建的節點添加到父節點的子節點列表的末尾,replaceChild
用於替換父節點的一個子節點。let newElement = document.createElement('div'); // 創建一個新的div元素 newElement.innerText = '這是新創建的元素'; // 設置新元素的文本內容 parentElement.appendChild(newElement); // 將新元素添加為parentElement的子節點 let anotherElement = document.createElement('span'); // 創建另一個元素 parentElement.replaceChild(anotherElement, newElement); // 替換父元素中的子元素
通過這些 API,可以方便地在 DOM 中修改元素的內容和結構。
在 DOM 中修改元素的樣式和屬性主要涉及以下幾個 API:
-
修改樣式:
style
屬性- 每個 DOM 元素都有一個
style
屬性,它允許你通過 JavaScript 直接修改元素的樣式。可以通過設置元素的style
屬性中的 CSS 屬性來改變樣式。
element.style.backgroundColor = 'red'; // 直接設置元素的背景顏色為紅色 element.style.fontSize = '20px'; // 設置元素的字體大小為20像素
- 每個 DOM 元素都有一個
-
修改類:
classList
屬性classList
提供了一種簡單的方法來操作元素的類名集合。常用的方法包括add()
,remove()
,toggle()
和contains()
。
element.classList.add('new-class'); // 添加一個新的類名 element.classList.remove('old-class'); // 移除一個類名 element.classList.toggle('active'); // 如果存在則刪除該類名,不存在則添加
-
設置和獲取屬性:
setAttribute()
和getAttribute()
- 這兩個方法分別用於設置和獲取元素的屬性。屬性可以是標準的 HTML 屬性,如
id
,src
,href
等,也可以是自定義屬性。
element.setAttribute('data-custom', 'value'); // 設置一個自定義屬性 let value = element.getAttribute('data-custom'); // 獲取這個自定義屬性的值
- 這兩個方法分別用於設置和獲取元素的屬性。屬性可以是標準的 HTML 屬性,如
-
直接修改屬性
- 對於一些常用的 HTML 屬性,如
id
,src
,href
等,可以直接通過元素對象來獲取和設置。
element.href = 'https://example.com'; // 直接設置元素的href屬性 let elementId = element.id; // 直接獲取元素的id屬性
- 對於一些常用的 HTML 屬性,如
通過這些 API,可以方便地在 JavaScript 中修改 DOM 元素的樣式和屬性,以實現動態的頁面效果。
如何替換元素#
替換元素#
在 DOM 操作中,替換元素涉及以下幾個常用的 API:
-
replaceChild(newChild, oldChild)
- 這是一個在父節點上調用的方法,用於將父節點中的一個舊子節點替換為一個新的子節點。
newChild
是要插入的新節點,而oldChild
是要被替換的舊節點。
parentNode.replaceChild(newElement, oldElement); // 將parentNode中的oldElement替換為newElement
- 這是一個在父節點上調用的方法,用於將父節點中的一個舊子節點替換為一個新的子節點。
-
replaceWith(...)
replaceWith()
方法允許你將一個 DOM 節點(或多個節點)替換為指定的節點或者一段 HTML 或文本字符串。這個方法是直接在要被替換的節點上調用的。
oldElement.replaceWith(newElement); // 將oldElement替換為newElement
-
使用
insertBefore()
配合removeChild()
或remove()
來實現替換- 雖然不是直接的替換方法,但可以通過先插入新節點到舊節點之前,然後移除舊節點,達到替換的效果。
parentNode.insertBefore(newElement, oldElement); // 將newElement插入到oldElement之前 parentNode.removeChild(oldElement); // 移除oldElement,實現替換效果 // 或者使用更簡潔的 oldElement.remove(),如果環境支持 oldElement.remove();
-
使用
outerHTML
- 通過設置元素的
outerHTML
屬性,可以直接替換整個元素,包括元素本身及其內容。這種方法不需要先獲取父節點。
oldElement.outerHTML = '<div id="newElement">新內容</div>'; // 用新的HTML內容替換oldElement,包括元素本身
- 通過設置元素的
這些 API 提供了靈活的方式來替換 DOM 中的元素,可以根據不同的場景和需求選擇合適的方法。
如何給元素添加事件#
在 DOM 中處理元素事件主要涉及以下幾個 API:
-
addEventListener(event, handler, [options])
- 用於在指定元素上添加一個事件監聽器。
event
是要監聽的事件類型(如"click"
,"mouseover"
等),handler
是事件發生時調用的函數,options
是一個可選的參數,用於指定更詳細的事件監聽行為。
element.addEventListener('click', function() { console.log('元素被點擊了'); });
- 用於在指定元素上添加一個事件監聽器。
-
removeEventListener(event, handler, [options])
- 用於移除之前通過
addEventListener
添加的事件監聽器。需要注意的是,handler
必須與添加監聽器時使用的是同一個函數引用。
function handleClick() { console.log('元素被點擊了'); } element.addEventListener('click', handleClick); // 稍後移除監聽器 element.removeEventListener('click', handleClick);
- 用於移除之前通過
-
dispatchEvent(event)
- 用於觸發指定元素上的事件。
event
是一個Event
對象的實例,可以通過new Event()
來創建。
let event = new Event('customEvent'); element.dispatchEvent(event); // 觸發自定義事件
- 用於觸發指定元素上的事件。
-
通過屬性直接分配事件處理器
- 可以直接將事件處理函數分配給元素的事件處理屬性。例如,
onclick
對應於點擊事件。
element.onclick = function() { console.log('元素被點擊了'); };
- 可以直接將事件處理函數分配給元素的事件處理屬性。例如,
-
使用
on
前綴的屬性來設置事件處理函數- 除了
onclick
,還有許多其他的事件可以通過設置以on
開頭的屬性來添加處理函數,例如onmouseover
、onkeydown
等。
element.onmouseover = function() { console.log('鼠標懸停在元素上'); };
- 除了
-
使用
Event
對象- 事件處理函數中的
event
參數是一個Event
對象的實例,它提供了關於事件的信息,如觸發事件的元素、事件類型以及其他與特定事件相關的屬性和方法。
element.addEventListener('click', function(event) { console.log('點擊發生在 ' + event.target.tagName + ' 元素上'); });
- 事件處理函數中的
-
preventDefault()
- 事件對象的
preventDefault()
方法用於阻止事件的默認行為。這在處理如點擊鏈接時不希望頁面跳轉,或在提交表單時不希望頁面重新加載等場景下非常有用。
element.addEventListener('click', function(event) { event.preventDefault(); // 阻止鏈接默認的跳轉行為 });
- 事件對象的
-
stopPropagation()
- 用於阻止事件冒泡到父元素。事件冒泡是指事件從最深的節點開始,然後逐級向上傳播到較為淺的節點的過程。
element.addEventListener('click', function(event) { event.stopPropagation(); // 阻止事件繼續冒泡 });
-
事件委託
- 利用事件冒泡的特性,可以將事件監聽器設置在父元素上,而不是每個子元素上。當事件在子元素上觸發並冒泡到父元素時,可以通過檢查事件的
target
屬性來確定是哪些子元素觸發的事件。
parentElement.addEventListener('click', function(event) { if (event.target && event.target.matches('button.child')) { console.log('子按鈕被點擊'); } });
- 利用事件冒泡的特性,可以將事件監聽器設置在父元素上,而不是每個子元素上。當事件在子元素上觸發並冒泡到父元素時,可以通過檢查事件的
-
capture
事件捕獲- 在事件處理的第三個參數
options
中,可以設置capture
為true
來指定事件處理器在捕獲階段而不是冒泡階段執行。事件捕獲是指事件從最外層開始,然後逐級向下傳播到最深的節點的過程。
element.addEventListener('click', function() { console.log('捕獲階段的事件處理'); }, {capture: true});
- 在事件處理的第三個參數
-
once
選項- 在
addEventListener
的第三個參數中,設置once: true
可以讓事件處理器只執行一次,然後自動移除。
element.addEventListener('click', function() { console.log('這個事件處理器只會執行一次'); }, {once: true});
- 在
通過這些 API,可以靈活地為 DOM 元素添加、移除和觸發事件,實現豐富的交互效果。
生命週期#
DOM(文檔對象模型)和 Window 對象在 Web 頁面的生命週期中扮演著重要的角色。下面介紹一些與 DOM 和 Window 生命週期相關的 API:
-
Window 的生命週期事件:
-
load
事件- 當整個頁面及所有依賴資源如樣式表和圖片都已完成加載時,
window
對象會觸發load
事件。
window.addEventListener('load', function() { console.log('頁面完全加載完畢'); });
- 當整個頁面及所有依賴資源如樣式表和圖片都已完成加載時,
-
DOMContentLoaded
事件- 當初始的 HTML 文檔被完全加載和解析完成,不需要等待樣式表、圖片和子框架完成加載,
document
對象會觸發DOMContentLoaded
事件。
document.addEventListener('DOMContentLoaded', function() { console.log('DOM內容加載完畢'); });
- 當初始的 HTML 文檔被完全加載和解析完成,不需要等待樣式表、圖片和子框架完成加載,
-
unload
事件- 當用戶離開當前頁面,
window
對象會觸發unload
事件。這個事件可以用於清理工作,如關閉彈出窗口等。
window.addEventListener('unload', function() { console.log('用戶離開頁面'); });
- 當用戶離開當前頁面,
-
beforeunload
事件- 在窗口、文檔或其資源即將卸載時觸發。可以用來詢問用戶是否確定離開當前頁面,通常用於提示用戶保存未保存的更改。
window.addEventListener('beforeunload', function(event) { event.returnValue = '您有未保存的更改,確定要離開嗎?'; });
-
-
請求動畫幀(Request Animation Frame):
-
requestAnimationFrame(callback)
- 提供了一種在瀏覽器重繪之前調用特定代碼的方法,用於執行動畫或頁面重繪等。這個方法比傳統的
setInterval
更高效,可以更平滑地執行動畫。
function animate() { // 動畫代碼 requestAnimationFrame(animate); } requestAnimationFrame(animate);
- 提供了一種在瀏覽器重繪之前調用特定代碼的方法,用於執行動畫或頁面重繪等。這個方法比傳統的
-
-
頁面可見性 API(Page Visibility API):
- 這個 API 提供了
visibilitychange
事件,以及document.hidden
屬性,用於檢測頁面是否對用戶可見。這在優化頁面性能和用戶體驗方面特別有用,比如可以在頁面不可見時暫停視頻播放或停止執行動畫。
document.addEventListener('visibilitychange', function() { if (document.hidden) { console.log('頁面不可見'); } else { console.log('頁面可見'); } });
- 這個 API 提供了
-
性能監測 API(Performance API):
performance
對象允許訪問與當前頁面相關的性能數據。例如,可以使用performance.timing
來分析不同階段的耗時,如頁面加載、解析等。
window.addEventListener('load', function() { setTimeout(function() { const timing = performance.timing; const loadTime = timing.loadEventEnd - timing.navigationStart; console.log('頁面加載時間:' + loadTime); }, 0); });
-
resize
事件:- 當瀏覽器窗口被調整大小時,
window
對象會觸發resize
事件。可以用來調整頁面佈局或執行其他響應窗口大小變化的操作。
window.addEventListener('resize', function() { console.log('窗口大小變化了'); });
- 當瀏覽器窗口被調整大小時,
-
scroll
事件:- 當用戶滾動頁面時,會觸發
scroll
事件。這可以用於實現 “懶加載”(延遲加載圖片等內容),或者動態改變導航條的樣式等。
window.addEventListener('scroll', function() { console.log('頁面被滾動了'); });
- 當用戶滾動頁面時,會觸發
-
focus
和blur
事件:- 當頁面或頁面內的元素獲得或失去焦點時,會觸發
focus
和blur
事件。這可以用於改善表單輸入的用戶體驗,或在應用中管理鍵盤快捷鍵。
window.addEventListener('focus', function() { console.log('窗口獲得了焦點'); }); window.addEventListener('blur', function() { console.log('窗口失去了焦點'); });
- 當頁面或頁面內的元素獲得或失去焦點時,會觸發
通過結合使用這些 API,開發者可以更精細地控制和優化 Web 應用的行為和性能,提升用戶的互動體驗。
通過監聽和處理這些生命週期事件,可以更好地控制 Web 頁面的加載、渲染和卸載過程,提高用戶體驗。