JavaScript&DOM

DOM(Document of Model)

文檔對象類型用樹形結構完整的呈現了html文檔,包括元素之間的關係和組成元素的內容及屬性

瀏覽器如何處理html文檔: how browser handle html document

received html: server respoend with html no matter what it received.
html tags are converted to tokens
tokens are converted to nodes:令牌被轉換成樹節點
nodes are converted to the Dom 

按id返回頁面單一元素

document.getElementById(‘footer’)

Dom中id是唯一的,所以個通過id值返回一個唯一的對象.
id字符串區分大小寫
返回查找元素的所有子節點

一次返回多個元素

document.getElementByClassName(‘classname’);

返回所有包含此classname的htmlCollection
document.getElementsByClassName(‘line-numbers language-html’)
多個classname以空格符分隔
rootElement.getElementsByClassName(‘classname’);
可以被任何的根元素調用

document.getElementsByTagName(‘tagname’);

返回所有指定標籤名tag的htmlCollection,
只有後代元素會被搜索
標籤名應使用小寫
document.getElementById(“toc”).getElementsByTagName(“*”)
可以被任何根元素調用,* 返回所有元素

Nodes,Elements and Interfaces

Node是類,繼承自EventTarget,是樹結構的中間節點,包含實例node的所有屬性和方法
Element是類,繼承自Node,是樹結構的根節點,同樣是Node,但是element實例具有具體屬性和方法
JavaScript Interface主要指Web APIs

Note: $0 默認指代頁面上選中的元素

more way to access element

.querySelector() - returns a single element

// find and return the element with an ID of "header"
document.querySelector('#header');
// find and return the first element with the class "header"
document.querySelector('.header');
// find and return the first <header> element
document.querySelector('header');

.querySelectorAll() - returns a list of elements

// find and return the element with an ID of "header"
document.querySelector('#header');
// find and return a list of elements with the class "header"
document.querySelectorAll('.header');

const allHeaders = document.querySelectorAll('header');
for(let i = 0; i < allHeaders; i++){
    console.dir(allHeaders[i]);
}

JavaScrip創建內容:增,刪,改DOM節點

更新已存在的頁面內容

 innerhtml 返回子節點的html code

.innerhtml:
返回當前選中元素所有子元素的html內容,返回值是string
設置元素的html內容
.outerhtml:
返回當前選中元素,及其所有子元素的html內容
<h1 id="pick-me">Greetings To <span>All</span>!</h1>
const innerResults = document.querySelector('#pick-me').innerHTML;
console.log(innerResults); // logs the string: "Greetings To <span>All</span>!"
const outerResults = document.querySelector('#pick-me').outerHTML;
console.log(outerResults); // logs the string: "<h1 id="pick-me">Greetings To <span>All</span>!</h1>"

textContent 返回節點所有未經css修飾的文本內容

.textContent
返回當前選中元素及其子元素的文本內容,返回值是string或null
設置文本內容,html標籤作爲文本被顯示
myElement.textContent = 'The <strong>Greatest</strong> Ice Cream Flavors'; // doesn't work as expected
myElement.innerHTML = 'The <strong>Greatest</strong> Ice Cream Flavors'; // works as expected

innerText 返回頁面呈現的文本內容

.innerText
返回頁面顯示的文本內容,行內css和html標籤不會顯示
設置元素的顯示文本內容,其中的css樣式和html標籤不會進行渲染,會被顯示出來

新增頁面內容 add new page content

createElement

根據html標籤創建元素,調用對象是document,所以並不會添加新創建的元素到頁面(DOM)中
// creates and returns a <span> element
var newspan = document.createElement('span');
newspan.innerhtml = '<em> this is a new span string </em>';

appendChild

發起調用的必須是一個具體的元素,新的元素將被追加到調用元素的最後一個子元素
h1element=document.querySelector(‘h1’);
h1element.appendChild(newspan);

Note:
  1. 更新文本內容來說,與其新增元素並追加不如直接更新已有元素的textContent屬性
  2. 新建的元素只能被追加一次
    const mainHeading = document.querySelector('#main-heading');//123
    const otherHeading = document.querySelector('#other-heading');//456
    const excitedText = document.createElement('span');
    excitedText.textContent = '!!!';
    mainHeading.appendChild(excitedText);//123
    otherHeading.appendChild(excitedText);//456!!!

insertAdjacentHTML

element.insertAdjacentHTML(position, text);
解析text字符串爲html或xml節點,然後插入到調用元素的指定位置
position的可視化示例:
<!-- beforebegin --><p><!-- afterbegin -->foo
<!-- beforeend --></p><!-- afterend -->


var h1element = document.querySelector('h1');
// "<h1 class="white mb-half" style="" > Learn ARKit </h1>"
h1element.insertAdjacentHTML("afterbegin",'<div>asdfasd</div>');
//"<h1 class="white mb-half" style=""><div>asdfasd</div>Learn ARKit</h1>"

刪除頁面內容

removeChild

var oldChild = node.removeChild(child);
父元素node刪除其子元素child
oldChild指向被刪除的子元素child,child不再DOM中.
如果沒有oldChild引用,則仍會存在內存中一段時間,最終會自動刪除

remove

node.remove();
從node的父元素中刪除node
等價於: node.parentElement.removeChild(node);

ParentNode.firstElementChild
返回父元素的第一個子元素
ParentNode.firstChild
如果有,則返回空白符或回車符

樣式頁面內容 style page content

.style. 一次只能修改一個css樣式屬性

document.querySelector('h1').style.color = 'red'

CSS優先級 CSS specificity
擁有最高級別的css屬性將被應用,樣式規則距離元素越近,級別就越高.
樣式表中的樣式

Note: !important修飾的樣式擁有最最高級別,核武器慎用

.cssText() 一次可以更新多個樣式屬性

Note:csstext將會覆蓋原有的樣式屬性
設置的字符串必須與樣式表中的css樣式一致
const mainHeading = document.querySelector('h1');
mainHeading.style.cssText = 'color: blue; background-color: orange; font-size: 3.5em';

.setAttribute() 一次可以設置多個樣式屬性

還可以設置非樣式屬性,例如 ID
document.querySelectorAll('h6')[4].setAttribute('id','index4ofh6');
//document.querySelectorAll('h6')[4].setAttribute('id','index4ofh6')
document.querySelector('#index4ofh6').setAttribute('style','color: red; font-size:2em;')
//document.querySelector('#index4ofh6').setAttribute('style','color: red; font-size:2em;')

.className

var cName = elementNodeReference.className;
獲取指定元素的類的字符串
elementNodeReference.className = cName;
設置元素類,會覆蓋掉原有的類的值
var listOfClasses = document.querySelector('#main-heading');// large white
操作數組:
const arrayOfClasses = listOfClasses.split(' ');//(2) ["large", "white"]
arrayOfClasses.push("white2"); //3
for(let i=0; i < arrayOfClasses.length; i++){ console.log(arrayOfClasses[i]);};//large white white2
arrayOfClasses.pop("white2"); //white2

.classList

const elementClasses = elementNodeReference.classList;
將元素的類的信息以DOMTokenList數據結構返回
DOMTokenList結構有自己的默認方法:
add( String [, String [, …]] ), remove( String [, String [, …]] ) 要刪除的類不存在則報錯
item( Number ), contains( String ), replace( oldClass, newClass )
toggle( String [, force] ) 如果類存在則刪除,不存在則增加;若第二個參數存在,根據其bool值進行增刪
document.querySelector('h6.text-center').classList
//DOMTokenList ["text-center", value: "text-center"]
document.querySelector('h6.text-center').classList.toggle('example')//true
//DOMTokenList(2) ["text-center", "example", value: "text-center example"]

瀏覽器事件 Working with browser events

DOM事件(Events)被髮送用於通知代碼相關的事情已經發生了.
每個事件都是繼承自Event 類的對象,可以包括自定義的成員屬性及函數用於獲取事件發生時相關的更多信息.
事件可以表示從基本用戶交互到渲染模型中發生的事件的自動通知的所有內容.

查看事件

Chrome瀏覽器:monitorEvents(document)
monitorEvents(document); // start displaying all events on the document object
unmonitorEvents(document); // turn off the displaying of all events on the document object.

事件交互

EventTarget:被對象實現的接口,可以接收events和添加監聽
EventTarget是頂級接口,document,Element,window等都是常見的EventTarget,都繼承於此
EventTarget沒有屬性,只有三個方法
.addEventListener()
.removeEventListener()
.dispatchEvent()

Adding An Event Listener

<target>.addEventListener(<type>, <listener>)
document.querySelector(‘h1’).addEventListener(‘click’,function(){
console.log('this is an h1 element is being clicked');
})
target: 事件的目標,瀏覽器中的任意元素: document, h1, class, id...
type:listen-for, user operate type, click, dbclick...
listener:function-to-run-when-an-event-happens, function(){}

event lists

去掉事件的監聽方法

equality comparisons and sameness

JavaScript如何比較兩個對象是否相等
== 先強制轉換成一致的類型 1==‘1’ //true
=== 不進行類型轉換 1===‘1’ //false
objects, arrays, and functions:
var a = {myFunction: function quiz() { console.log('hi'); }};
var b = {myFunction: function quiz() { console.log('hi'); }};
a.myFunction === b.myFunction //false
function quiz() { ... }
var a = {myFunction: quiz};
var b = {myFunction: quiz};
a.myFunction === b.myFunction //true

remove an event listener

target.removeEventListener(type, listener[, options]);
要去掉添加在某個對象上的監聽方法,需要使用添加時的方法,直接去掉方法的字面定義,不起作用
var clickfun = function () { console.log('this is a dblclick event');};
document.addEventListener('dblclick', clickfun);
document.removeEventListener('dblclick', clickfun);

Phases of an Events 事件的階段

事件的生命週期 UI Events

定位階段:事件對象通過節點傳播到目標節點: window,父節點1,父節點2,… 目標節點
目標階段:事件對象定位到目標節點上
冒泡階段:事件對象從目標節點反向傳播到window.每個節點上的事件會被觸發,可設置爲不觸發

設置事件觸發

.addEventListener()只有兩個參數時,默認是在冒泡階段觸發
三個參數,且爲true時,在定位階段就會執行
document.addEventListener('click', function () {console.log('The document was clicked');}, true);
Test
document.addEventListener('click' , function(){console.log('doc');});
document.body.addEventListener('click' , function(){console.log('doc body');});
//單擊界面:result:doc body/ doc
document.body.addEventListener('click' , function(){console.log('doc2');}, true);
//單擊界面:result:doc2/ doc body/

事件對象 The Event Object

  1. 每當單擊,雙擊等事件發生,瀏覽器會自動包括一個事件對象,即標準的javascript對象,其中包含了大量事件自身的信息
    在監聽方法的參數位置添加: event,e,evt,theEvent,horse
    document.addEventListener(‘click’, function(event){console.log(event)});
  2. bind(this)
    var something = function (element) {
    this.name = 'something';
    this.onclick1 = function (event){
    console.log(this.name);};
    this.onclick2 = function (event){
    console.log(this.name);};
    element.addEventListener('click' , this.onclick1, false);//undefined, this 指代傳入的body
    element.addEventListener('click' , this.onclick2.bind(this), false);//something, this 指代新創建的s對象
    }
    var s = new Something(document.body);
  3. preventDefault() 阻止對象的默認事件
    例如checkbox默認選中的功能
    document.querySelector("#id-checkbox").addEventListener("click", function(event) {
    document.getElementById("output-box").innerHTML += "Sorry! <code>preventDefault()</code> won't let you check this!<br>";
    event.preventDefault();
    }, false);

Avoid Too Many Events 避免過多的事件

事件委託 Event Delegation

事件委託是一種委託給父節點處理子節點事件能力的方式
原因:父節點有多個子節點時,避免太多的子節點事件
實現:事件的3個階段(冒泡階段), 事件對象(參數)及.target屬性
document.querySelector(‘#content’).addEventListener(‘click’, function (evt) {
// convert nodeName to lowercase
if (evt.target.nodeName.toLowerCase() === ‘span’) {
console.log(‘A span was clicked with text ’ + evt.target.textContent);
}
});

Know when the DOM is ready

Javascript中操作的元素需要已被加載到DOM中,否則會報錯.
解決:1, 將javascript snippet 放到最後執行
2, 調用事件 DOMContentLoaded
當初始的 HTML 文檔被完全加載和解析完成之後,DOMContentLoaded 事件被觸發,而無需等待樣式表、圖像和子框架的完成加載.
load 應該僅用於檢測一個完全加載的頁面
window.onload = function () {
console.log(‘log: window.load’);
}
document.addEventListener(‘load’, new function(event){console.log(‘log: document.load’);});
document.addEventListener(‘DOMContentLoaded ‘,
new function(event){console.log(‘log: document.DOMContentLoaded ‘);});
// log: document.load
// log: document.DOMContentLoaded
// log: window.load

Performance 性能

Add page content efficiently 高效的添加頁面內容

Javascript衡量語句的執行時間,window.performance.now()

let t01 = performance.now();
for (let i = 1; i <= 100; i++) {
for (let j = 1; j <= 100; j++) {
console.log(‘i and j are ‘, i, j);
}
}
let t02 = performance.now();
console.log(‘this code took ’ + (t02 - t01) +’ milliseconds.’);

DocumentFragment

DocumentFragment 接口表示一個沒有父級文件的最小文檔對象.
它被當做一個輕量版的 Document 使用,作爲參數被添加(append)或被插入(inserted)的是片段的所有子節點, 而非片段本身.
因爲所有的節點會被一次插入到文檔中,而這個操作僅發生一個重渲染的操作
const fragment = document.createDocumentFragment(); // ← uses a DocumentFragment instead of a


for (let i = 0; i < 200; i++) {
const newElement = document.createElement(‘p’);
newElement.innerText = ‘This is paragraph number ’ + i;
fragment.appendChild(newElement);
}
document.body.appendChild(fragment); // reflow and repaint here – once!

repaint & reflow 重繪和迴流

重繪: 發生在改變元素外觀及可見性,但是不會影響佈局的情況下.例如:outline, visibility, background, or color
迴流: 發生在影響頁面佈局(包括部分頁面及整體頁面)的情況下.例如:width, height, font-family, font-size.
迴流會導致元素所有子節點及相關上級節點的迴流,其右節點也會迴流
避免迴流: 使用類樣式, 使用虛擬DOM,DomcumentFragment替換容器div
避免多行內聯樣式,每一個樣式都會引發一次迴流,外部樣式類只會引起一次
使用固定或絕對位置的動畫,引起重繪但不會發生迴流
避免table進行佈局,其中的所有元素都會迴流
不在css中使用javascript表達式

The call stack 回調棧

單線程: 一次處理一個指令,javascript是單線程的
調用堆棧:指令執行是順序的.每當程序運行,方法棧中會入棧main()方法,它會一直保持運行.
每當有其他方法被調用,方法棧中會入棧對應的方法,方法執行完成後會對應的出棧

The event loop 事件循環

同步:code在同一時間存在和發生,所在即執行
JavaScript event loop: javascript事件循環.
Javascript併發模型:run-to-completion:如果有代碼塊在運行,就一直運行完成爲止
event loop:如果調用堆棧中沒有調用方法了,就去追加事件隊列中的任何事件處理.
選取事件隊列隊首的事件,運行它的處理代碼,重複下一個事件

setTimeout

.addEventListener()添加的code會在後面事件觸發時執行
setTimeout()則可以指定具體延遲的時間
setTimeout(function sayHi() {console.log(‘Howdy’);}, 0); //可以斷開長語句,允許瀏覽器處理用戶交互,不至於讓用戶長時間的等待

let count = 1
function generateParagraphs() {
    const fragment = document.createDocumentFragment();
    for (let i = 1; i <= 500; i++) {
        const newElement = document.createElement('p');
        newElement.textContent = 'This is paragraph number ' + count;
        count = count + 1;
        fragment.appendChild(newElement);
    }

    document.body.appendChild(fragment);
    if (count < 20000) {setTimeout(generateParagraphs, 0);}
}

generateParagraphs();

JavaScript
Standards
https://www.ecma-international.org/ecma-262/#sec-intro
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide
Framework
http://todomvc.com/
Backbone.js
http://backbonejs.org/#
W3c specification
https://www.w3.org/standards/techs/dom#w3c_all
Api
https://developer.mozilla.org/en-US/docs/Web/API/
https://developer.mozilla.org/en-US/docs/Web/API/Element
https://developer.mozilla.org/en-US/docs/Web/API/Node
https://developer.mozilla.org/en-US/docs/Web/CSS/Specificity

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章