在html中運行js的3種方式:node
<!--1--> <h1>Testing alert</h1> <script>alert("hello!");</script> <!--2--> <h1>Testing alert</h1> <script src="code/hello.js"></script> <!--3--> <button onclick="alert('Boom!');">DO NOT PRESS</button>
js在瀏覽器中解釋、運行,瀏覽器對js具備諸多限制,例如不容許js訪問本地文件、不能修改任何和所訪問網頁無關的東西。所以js彷彿是在一個用鋼板圍起來的沙盒裏活動,而html會被瀏覽器獲取、解析爲文檔模型(一種特殊的數據結構),瀏覽器依據這個文檔模型來渲染畫面,這個文檔模型是在js沙盒範圍內的東西,js能夠自由地讀取和修改它。數組
html被解析爲一種叫文檔對象模型(Document Object Model)的東西,瀏覽器就是依據它來進行頁面渲染的。全局綁定document是咱們訪問這些文檔對象的入口,它的documentElement屬性指向html標籤所對應的對象,還有head和body屬性,分別指向各類對應的對象。DOM是一種樹形結構,document.documentElement便是根節點。再好比說document.body也是一個節點,它的孩子能夠是元素節點,也多是一段文本或者評論節點。數據結構
每一個DOM節點對象都有一個nodeType屬性,該屬性包含標識節點類型的編碼(數字)。元素的編碼爲Node.ELEMENT_NODE,該常量的真實值爲數字1。 表示文檔中一段文本的文本節點編碼是3(Node.TEXT_NODE)。註釋的編碼爲8(Node.COMMENT_NODE)。app
DOM的可視化:ide
葉子是文本節點,箭頭則代表了父子關係。函數
DOM並不是是專門爲js設計的,它最開始是想成爲xml同樣中立的東西。因此它自己和js集成得並非特別好。一個典型的例子是childNodes屬性,它相似array,卻又不是真正的array,而是NodeList類型,因此它並不支持slice和map。再好比,DOM並無提供一次性建立節點並添加子節點、屬性的方法,而是必需要首先建立它,而後一個一個地添加子節點和屬性。
固然,這些缺點都不是致命的,咱們顯然能夠自定義函數來封裝一些操做,以便更好地和DOM交互。(已經有許多現成的庫作這件事情)
若是屬性對應的節點不存在,則爲null。
有個與childNodes相似的children屬性,不過children屬性只包含元素子女(編碼爲1的節點),當你對文本節點不感興趣的時候這一般是有用的。能夠經過遞歸來遍歷DOM樹,由於childNodes不是真正的數組,所以只能經過常規的循環來遍歷它,不能用for/of循環。
<!doctype html> <html> <head> <title>My home page</title> </head> <body> <h1>My home page</h1> <p>Hello, I am Marijn and this is my home page.</p> <p>I also wrote a book! Read it <a href="http://eloquentjavascript.net">here</a>.</p> <script type="text/javascript"> // 遞歸,深度優先搜索DOM樹 function talksAbout(node, string) { if (node.nodeType == Node.ELEMENT_NODE) { for (let i = 0; i < node.childNodes.length; i++) { if (talksAbout(node.childNodes[i], string)) { return true; } } return false; } else if (node.nodeType == Node.TEXT_NODE) { // text節點的nodeValue儲存着顯示文本 return node.nodeValue.indexOf(string) > -1; } } console.log(talksAbout(document.body, "book")); // → true </script> </body> </html>
根據子元素的標籤類型:
let link = document.body.getElementsByTagName("a")[0];
console.log(link.href);
根據id:
<p>My ostrich Gertrude:</p> <p><img id="gertrude" src="img/ostrich.png"></p> <script> let ostrich = document.getElementById("gertrude"); console.log(ostrich.src); </script>
相似的還有getElementsByClassName
remove,移除元素:
<p>One</p> <p>Two</p> <p>Three</p> <script> let paragraphs = document.body.getElementsByTagName("p"); paragraphs[0].remove(); // two/three </script>
appendChild,用於插入元素到某個父元素的孩子元素的最後一位:
<p>One</p> <p>Two</p> <p>Three</p> <script> let paragraphs = document.body.getElementsByTagName("p"); document.body.appendChild(paragraphs[1]); // One/Three/Two </script>
insertBefore,一個節點只能存在於文檔中的一個地方,插入段落3會致使它在原來的位置移除,再被插入到相應位置:
<p>One</p> <p>Two</p> <p>Three</p> <script> let paragraphs = document.body.getElementsByTagName("p"); document.body.insertBefore(paragraphs[2], paragraphs[0]); // insertBefore插入到某某以前,這裏是把three插到one以前 // Three/One/Two </script>
【All operations that insert a node somewhere will, as a side effect, cause it to be removed from its current position (if it has one).】
相似的還有replaceChild :
<p>One</p> <p>Two</p> <p>Three</p> <script> let paragraphs = document.body.getElementsByTagName("p"); document.body.replaceChild(paragraphs[0], paragraphs[1]); // 第一個做爲新元素,第二個是被淘汰的舊元素 // One/Three </script>
點擊按鈕後將全部圖片替換成其alt屬性中的文字:
<p>The <img src="img/cat.png" alt="Cat"> in the <img src="img/hat.png" alt="Hat">.</p> <p><button onclick="replaceImages()">Replace</button></p> <script> function replaceImages() { let images = document.body.getElementsByTagName("img"); // image的長度內容都是隨着document動態變化的 for(let i = images.length - 1; i >= 0; i--) { // 因此纔要從最後一個元素開始替換,這樣移除元素 // 的時候纔不會影響到訪問其他兄弟元素的下標值 let image = images[i]; if(image.alt) { let text = document.createTextNode(image.alt); image.parentNode.replaceChild(text, image); // 必須得到父元素的引用才能替換孩子元素 } } // 主題無關↓:從DOM獲取一個靜態的數組Array.from let arrayish = { 0: "one", 1: "two", length: 2 }; let array = Array.from(arrayish); console.log(array.map(s => s.toUpperCase())); // → ["ONE", "TWO"] } </script>
document.createElement(tag name)返回一個相應類型的空元素,示例以下:
<blockquote id="quote"> No book can ever be finished. While working on it we learn just enough to find it immature the moment we turn away from it. </blockquote> <script> // 建立一個type類型的空元素 // children是它的孩子節點, // 該函數能夠接受n個參數 function elt(type, ...children) { let node = document.createElement(type); for(let child of children) { if(typeof child != "string") node.appendChild(child); else node.appendChild(document.createTextNode(child)); // 若是是string類型就將其轉換爲一個文本節點再插入 } return node; // 返回這個建立好的節點 } document.getElementById("quote").appendChild( elt("footer"/*type*/, "—"/*child-0*/, elt("strong", "Karl Popper")/*child-1*/, ", preface to the second editon of "/*child-2*/, elt("em", "The Open Society and Its Enemies")/*child-3*/, ", 1950"/*child-4*/)); </script>
不只能夠在js中設置、獲取元素的標準屬性,還能夠用getAttribute和setAttribute方法設置自定義的屬性
(諸如alt、href等標準屬性,直接node.alt就能夠訪問)
<p data-classified="secret">The launch code is 00000000.</p> <p data-classified="unclassified">I have two feet.</p> <script> let paras = document.body.getElementsByTagName("p"); for(let para of Array.from(paras)) { // 將動態的nodelist轉化爲靜態的數組 if(para.getAttribute("data-classified") == "secret") { // 推薦用前綴修飾自定義屬性,這樣就不會與標準屬性衝突了 para.remove(); } } </script>
getAttribute和setAttribute和屬於通用方法,也能夠用來訪問標準屬性。
訪問元素所佔空間大小:
<p style="border: 3px solid red"> I'm boxed in </p> <script> let para = document.body.getElementsByTagName("p")[0]; console.log("clientHeight:", para.clientHeight); // 21 不包括邊框 console.log("offsetHeight:", para.offsetHeight); // 27 加上邊框 2*3px </script>
訪問元素位置最有效的方法:
<p style="border: 3px solid red"> I'm boxed in </p> <script> let para = document.body.getElementsByTagName("p")[0]; let boundingClientRect = para.getBoundingClientRect(); // html元素左上角相對於瀏覽器顯示屏的準確位置 console.log("top:", boundingClientRect.top); // 16 console.log("buttom:", boundingClientRect.buttom); // undefined console.log("left:", boundingClientRect.left); // 8 console.log("right:", boundingClientRect.right); // 556 // 相對於document的位置,須要加上滾動條位置 console.log("pageXOffset:", window.pageXOffset); console.log("pageYOffset:", window.pageYOffset); </script>
進行文檔佈局須要作不少工做。爲了速度,瀏覽器引擎不會在每次更改文檔時當即從新佈局文檔,而是儘量長時間的等待。當更改文檔的JavaScript程序完成運行時,瀏覽器才必須計算新的佈局以將更改的文檔繪製到屏幕上。當程序經過諸如offsetHeight或者getBoundingClientRect讀取某個東西的大小或者位置時,提供正確的信息也須要計算佈局。在讀取DOM佈局信息和更改DOM之間反覆交替的程序會強制執行大量佈局計算,所以運行速度很是慢↓
<script> function time(name, action) { let start = Date.now(); // Current time in milliseconds action(); console.log(name, "took", Date.now() - start, "ms"); } time("naive", () => { let target = document.getElementById("one"); while(target.offsetWidth < 2000) { target.appendChild(document.createTextNode("X")); } }); // → naive took 32 ms time("clever", function() { let target = document.getElementById("two"); target.appendChild(document.createTextNode("XXXXX")); let total = Math.ceil(2000 / (target.offsetWidth / 5)); target.firstChild.nodeValue = "X".repeat(total); }); // → clever took 1 ms </script>
<p id="para" style="color: purple"> Nice text </p> <script> let para = document.getElementById("para"); console.log(para.style.color); para.style.color = "magenta"; para.style['color'] = "red"; para.style.fontFamily = "cursive"; </script>
<p>And if you go chasing <span class="animal">rabbits</span></p> <p>And you know you're going to fall</p> <p>Tell 'em a <span class="character">hookah smoking <span class="animal">caterpillar</span></span></p> <p>Has given you the call</p> <script> function count(selector) { return document.querySelectorAll(selector).length; } console.log(count("p")); // All <p> elements // → 4 console.log(count(".animal")); // Class animal // → 2 console.log(count("p .animal")); // Animal inside of <p> // → 2 console.log(count("p > .animal")); // Direct child of <p> // → 1 </script>
querySelectorAll與querySelector後者只返回一個元素(第一個)。
<p>_</p> <p>_</p> <p>_</p> <p>_</p> <p>_</p> <p style="text-align: center"> <img src="img/cat.png" style="position: relative"> </p> <script> let cat = document.querySelector("img"); let angle = Math.PI / 2; function animate(time, lastTime) { if (lastTime != null) { angle += (time - lastTime) * 0.001; } cat.style.top = (Math.sin(angle) * 20) + "px"; cat.style.left = (Math.cos(angle) * 200) + "px"; requestAnimationFrame(newTime => animate(newTime, time)); } debugger; requestAnimationFrame(animate); </script>
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> </head> <body> <h1>Mountains</h1> <div id="mountains"></div> <script> const MOUNTAINS = [ {name: "Kilimanjaro", height: 5895, place: "Tanzania"}, {name: "Everest", height: 8848, place: "Nepal"}, {name: "Mount Fuji", height: 3776, place: "Japan"}, {name: "Vaalserberg", height: 323, place: "Netherlands"}, {name: "Denali", height: 6168, place: "United States"}, {name: "Popocatepetl", height: 5465, place: "Mexico"}, {name: "Mont Blanc", height: 4808, place: "Italy/France"} ]; // Your code here function elt(type, ...children) { let node = document.createElement(type); for(let child of children) { if(typeof child != "string" && typeof child != "number") { node.appendChild(child); } else node.appendChild(document.createTextNode(child)); // 若是是string類型就將其轉換爲一個文本節點再插入 } return node; // 返回這個建立好的節點 } let table = document.createElement("table"); let ths = document.createElement("tr"); ths.appendChild(elt("th", "name")); ths.appendChild(elt("th", "height")); ths.appendChild(elt("th", "place")); table.appendChild(ths); for (let mountain of MOUNTAINS) { let tds = document.createElement("tr"); tds.appendChild(elt("td", mountain['name'])); let tdHeight = elt("td", mountain['height']); tdHeight.style.textAlign = "right"; tds.appendChild(tdHeight); tds.appendChild(elt("td", mountain['place'])); table.appendChild(tds); } document.getElementById("mountains").appendChild(table); </script> </body> </html>
————--- -- - ---- —— ——- -- -- - -- - -- - -- - - - -
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title></title> </head> <body> <h1>Heading with a <span>span</span> element.</h1> <p>A paragraph with <span>one</span>, <span>two</span> spans. </p> <script> function byTagName(node, tagName) { // Your code here. let result = []; const byTagNameHelper = (node, tagName) => { for (let child of Array.from(node.children)) { if (child.nodeName == tagName.toUpperCase()) { result.push(child); } byTagNameHelper(child, tagName); } }; byTagNameHelper(node, tagName); return result; } console.log(byTagName(document.body, "h1").length); // → 1 console.log(byTagName(document.body, "span").length); // → 3 let para = document.querySelector("p"); console.log(byTagName(para, "span").length); // → 2 </script> </body> </html>
————--- -- - ---- —— ——- -- -- - -- - -- - -- - - - -
略