初学JavaScript--基础知识笔记(三)

xiaoxiao2021-02-28  125

DOM

由于HTML文档被浏览器解析后就是一棵DOM树,要改变HTML的结构,就需要通过JavaScript来操作DOM。

始终记住DOM是一个树形结构。操作一个DOM节点实际上就是这么几个操作:

更新:更新该DOM节点的内容,相当于更新了该DOM节点表示的HTML的内容;遍历:遍历该DOM节点下的子节点,以便进行进一步操作;添加:在该DOM节点下新增一个子节点,相当于动态增加了一个HTML节点;删除:将该节点从HTML中删除,相当于删掉了该DOM节点的内容以及它包含的所有子节点。

在操作一个DOM节点前,我们需要通过各种方式先拿到这个DOM节点。最常用的方法是document.getElementById()和document.getElementsByTagName(),以及CSS选择器document.getElementsByClassName()。

由于ID在HTML文档中是唯一的,所以document.getElementById()可以直接定位唯一的一个DOM节点。document.getElementsByTagName()和document.getElementsByClassName()总是返回一组DOM节点。要精确地选择DOM,可以先定位父节点,再从父节点开始选择,以缩小范围。

// 返回ID为'test'的节点: var test = document.getElementById('test'); // 先定位ID为'test-table'的节点,再返回其内部所有tr节点: var trs = document.getElementById('test-table').getElementsByTagName('tr'); // 先定位ID为'test-div'的节点,再返回其内部所有class包含red的节点: var reds = document.getElementById('test-div').getElementsByClassName('red'); // 获取节点test下的所有直属子节点: var cs = test.children; // 获取节点test下第一个、最后一个子节点: var first = test.firstElementChild; var last = test.lastElementChild;

第二种方法是使用querySelector()和querySelectorAll(),需要了解selector语法,然后使用条件来获取节点,更加方便:

// 通过querySelector获取ID为q1的节点: var q1 = document.querySelector('#q1'); // 通过querySelectorAll获取q1节点内的符合条件的所有节点: var ps = q1.querySelectorAll('div.highlighted > p');

更新DOM

可以直接修改节点的文本,有两种方法:

一种是修改innerHTML属性,这个方式非常强大,不但可以修改一个DOM节点的文本内容,还可以直接通过HTML片段修改DOM节点内部的子树:

// 获取<p id="p-id">...</p> var p = document.getElementById('p-id'); // 设置文本为abc: p.innerHTML = 'ABC'; // <p id="p-id">ABC</p> // 设置HTML: p.innerHTML = 'ABC <span style="color:red">RED</span> XYZ'; // <p>...</p>的内部结构已修改

用innerHTML时要注意,是否需要写入HTML。如果写入的字符串是通过网络拿到了,要注意对字符编码来避免XSS攻击。

第二种是修改innerText或textContent属性,这样可以自动对字符串进行HTML编码,保证无法设置任何HTML标签:

// 获取<p id="p-id">...</p> var p = document.getElementById('p-id'); // 设置文本: p.innerText = '<script>alert("Hi")</script>'; // HTML被自动编码,无法设置一个<script>节点: // <p id="p-id"><script>alert("Hi")</script></p>

两者的区别在于读取属性时,innerText不返回隐藏元素的文本,而textContent返回所有文本。注意IE<9不支持textContent。

修改CSS也是经常需要的操作。DOM节点的style属性对应所有的CSS,可以直接获取或设置。因为CSS允许font-size这样的名称,但它并非JavaScript有效的属性名,所以需要在JavaScript中改写为驼峰式命名fontSize:

// 获取<p id="p-id">...</p> var p = document.getElementById('p-id'); // 设置CSS: p.style.color = '#ff0000'; p.style.fontSize = '20px'; p.style.paddingTop = '2em';

插入DOM

当我们获得了某个DOM节点,想在这个DOM节点内插入新的DOM,应该如何做?

如果这个DOM节点是空的,例如,

,那么,直接使用innerHTML = ‘ child‘就可以修改DOM节点的内容,相当于“插入”了新的DOM节点。

如果这个DOM节点不是空的,那就不能这么做,因为innerHTML会直接替换掉原来的所有子节点。

有两个办法可以插入新的节点。

一个是使用appendChild,把一个子节点添加到父节点的最后一个子节点。例如:

<!-- HTML结构 --> <p id="js">JavaScript</p> <div id="list"> <p id="java">Java</p> <p id="python">Python</p> <p id="scheme">Scheme</p> </div>

例如添加到最后一项:

var js = document.getElementById('js'), list = document.getElementById('list'); list.appendChild(js);

现在,HTML结构变成了这样:

<!-- HTML结构 --> <div id="list"> <p id="java">Java</p> <p id="python">Python</p> <p id="scheme">Scheme</p> <p id="js">JavaScript</p> </div>

因为我们插入的js节点已经存在于当前的文档树,因此这个节点首先会从原先的位置删除,再插入到新的位置。

更多的时候我们会从零创建一个新的节点,然后插入到指定位置:

var list = document.getElementById('list'), haskell = document.createElement('p'); haskell.id = 'haskell'; haskell.innerText = 'Haskell'; list.appendChild(haskell);

这样我们就动态添加了一个新的节点:

<!-- HTML结构 --> <div id="list"> <p id="java">Java</p> <p id="python">Python</p> <p id="scheme">Scheme</p> <p id="haskell">Haskell</p> </div>

动态创建一个节点然后添加到DOM树中,可以实现很多功能。举个例子,下面的代码动态创建了一个

insertBefore

如果我们要把子节点插入到指定的位置怎么办?可以使用parentElement.insertBefore(newElement, referenceElement);,子节点会插入到referenceElement之前。

还是以上面的HTML为例,假定我们要把Haskell插入到Python之前:

<!-- HTML结构 --> <div id="list"> <p id="java">Java</p> <p id="python">Python</p> <p id="scheme">Scheme</p> </div>

可以这么写:

var list = document.getElementById('list'), ref = document.getElementById('python'), haskell = document.createElement('p'); haskell.id = 'haskell'; haskell.innerText = 'Haskell'; list.insertBefore(haskell, ref);

新的HTML结构如下:

<!-- HTML结构 --> <div id="list"> <p id="java">Java</p> <p id="haskell">Haskell</p> <p id="python">Python</p> <p id="scheme">Scheme</p> </div>

可见,使用insertBefore重点是要拿到一个“参考子节点”的引用。很多时候,需要循环一个父节点的所有子节点,可以通过迭代children属性实现:

var i, c, list = document.getElementById('list'); for (i = 0; i < list.children.length; i++) { c = list.children[i]; // 拿到第i个子节点 }

删除DOM

删除一个DOM节点就比插入要容易得多。

要删除一个节点,首先要获得该节点本身以及它的父节点,然后,调用父节点的removeChild把自己删掉:

// 拿到待删除节点: var self = document.getElementById('to-be-removed'); // 拿到父节点: var parent = self.parentElement; // 删除: var removed = parent.removeChild(self); removed === self; // true

注意到删除后的节点虽然不在文档树中了,但其实它还在内存中,可以随时再次被添加到别的位置。

当你遍历一个父节点的子节点并进行删除操作时,要注意,children属性是一个只读属性,并且它在子节点变化时会实时更新。

例如,对于如下HTML结构:

<div id="parent"> <p>First</p> <p>Second</p> </div>

当我们用如下代码删除子节点时:

var parent = document.getElementById('parent'); parent.removeChild(parent.children[0]); parent.removeChild(parent.children[1]); // <-- 浏览器报错

浏览器报错:parent.children[1]不是一个有效的节点。原因就在于,当

First

节点被删除后,parent.children的节点数量已经从2变为了1,索引[1]已经不存在了。

因此,删除多个节点时,要注意children属性时刻都在变化。

Node类型

每个节点都有一个nodeType属性,用于表明节点的类型。 + nodeName和nodeValue属性:要了解节点的具体信息,用这两个属性。对于元素节点,nodeName中保存的始终是元素的标签名,nodeValue的值始终是null。 + 每个节点都有一个childNodes属性,其中保存着一个NodeList对象。

下面的例子展示如何访问保存在NodeList中的节点:

var frstChild=someNode.childNodes[0]; var secondChild=someNode.childNodes.item(1); var count=someNode.childNodes.length;

可将NodeList对象转化成数组:

var arrayOfNodes=Array.prototype.slice.call(someNode.childNodes,0); appendChild()用于向childNode列表的末尾添加一个节点。insertBefore(),可以将节点插入指定位置,接收2个参数:要插入的节点和作为参照的节点。replaceChild(),可替换节点,接收2个参数:要插入的节点和要替换的节点。removeChile(),可移除节点,接收1个参数:要移除的节点。cloneNode(),用于创建调用这个方法的节点的一个完全相同的副本。接收一个布尔值参数,表示是否执行深复制。normalize(),处理文档树中的文本节点。

Document类型

document对象表示当前页面。由于HTML在浏览器中以DOM形式表示为树形结构,document对象就是整个DOM树的根节点。

document的title属性是从HTML文档中的xxx读取的,但是可以动态改变:

document.title = '努力学习JavaScript!';

要查找DOM树的某个节点,需要从document对象开始查找。最常用的查找是根据ID和Tag Name。

我们先准备HTML数据:

<dl id="drink-menu" style="border:solid 1px #ccc;padding:6px;"> <dt>摩卡</dt> <dd>热摩卡咖啡</dd> <dt>酸奶</dt> <dd>北京老酸奶</dd> <dt>果汁</dt> <dd>鲜榨苹果汁</dd> </dl>

用document对象提供的getElementById()和getElementsByTagName()可以按ID获得一个DOM节点和按Tag名称获得一组DOM节点:

getElementById()接收一个参数:要取得的元素的ID

getElementsByTagName()接收一个参数:要取得的元素的标签名

var menu = document.getElementById(‘drink-menu’); var drinks = document.getElementsByTagName(‘dt’); var i, s, menu, drinks;

menu = document.getElementById(‘drink-menu’); menu.tagName; // ‘DL’

drinks = document.getElementsByTagName(‘dt’); s = ‘提供的饮料有:’; for (i=0; i

Element类型

nodeType值为1nodeName的值为元素的标签名nodeValue的值为null

Element类型用于表现XML或HTML元素,提供了对元素标签名,子节点及特性的访问。

要访问元素的标签名,可以使用nodeName或tagName属性。

操作特性的DOM方法主要有3个:getAttribute(),setAttribute(),removeAttribute()。这三个方法可以针对任何特性使用。 + getAttribute()取得特性:传递给它的特姓名与实际的特姓名相同。 + setAttribute()设置特性,接收2个参数:要设置的特姓名和值。通过这个方法设置的特姓名会被统一换成小写行驶 + removeAttribute():彻底删除元素的特性。

attributes属性

getNamedItem(name):返回nodeName属性等于name的节点。removeNamedItem(name):从列表中移除nodeName属性等于name的节点。setNamedItem(node):向列表中添加节点,为索引。 attributes属性中包含一系列节点,每个节点的nodeName就是节点名称,节点nodeValue就是特性的值。

如果想要遍历元素的特性,attributes属性会方便一些:

function outputAttributes(element){ var pairs = new Array(), attrName, attrValue, i, len; for (i=0, len=element.attributes.length; i < len; i++){ attrName = element.attributes[i].nodeName; attrValue = element.attributes[i].nodeValue; pairs.push(attrName + "=\"" + attrValue + "\""); } return pairs.join(" "); } function getDivAtts(){ alert(outputAttributes(document.getElementById("myDiv"))); }

创建元素

document.createElement()可以创建新元素,接收一个参数:要创建元素的标签名。

function createNewElement(element){ var div = document.createElement("div"); div.id = "myNewDiv"; div.className = "box"; document.body.appendChild(div); }

元素的子节点

元素的childNodes属性中包含了它的所有子节点。

Text类型

nodeType的值为3nodeName的值为”#text”;nodeValue的值为节点所包含的文本;parentNode是一个Element;appendDate(text):将text添加到节点的末尾。deletDate(offset,count):从offset指定的位置开始删除count个字符。insertDate(offset,text):在offset指定的位置插入text。replaceDate(offset,count,text):用text替换从offset指定的位置开始到offset+count为止处的文本。splitText(offset):从offset指定的位置将当前文本节点分成两个文本节点。

substringDate(offset,count):提取从offset指定的位置开始到offset+count为止处的字符串。

function changeText(){ var div = document.getElementById("myDiv"); div.firstChild.nodeValue = "Some other message"; }

创建文本节点

可以用document.createTextNode()创建新文本节点,接收一个参数:要插入节点中的文本。在创建新节点的同时,也会为其设置ownerDocument属性。

只有把新节点添加到文档树中已经存在的节点中,才会在浏览器窗口中看到新节点。

function addNode(){ var element = document.createElement("div"); element.className = "message"; var textNode = document.createTextNode("Hello world!"); element.appendChild(textNode); document.body.appendChild(element); }

某些情况下也可能包含多个文本子节点:

function addNode(){ var element = document.createElement("div"); element.className = "message"; var textNode = document.createTextNode("Hello world!"); element.appendChild(textNode); var anotherTextNode = document.createTextNode("Yippee!"); element.appendChild(anotherTextNode); document.body.appendChild(element); }

规范化文本节点

normalize(),如果在一个包含多个文本节点的父元素调用该方法,会将所有文本节点合并成一个节点,结果节点的nodeValue等于将合并前每个文本节点的nodeValue值拼接起来的值。

function addNode(){ var element = document.createElement("div"); element.className = "message"; var textNode = document.createTextNode("Hello world!"); element.appendChild(textNode); var anotherTextNode = document.createTextNode("Yippee!"); element.appendChild(anotherTextNode); document.body.appendChild(element); alert(element.childNodes.length); //2 element.normalize(); alert(element.childNodes.length); //1 alert(element.firstChild.nodeValue); //"Hello World!Yippee!" }

分割文本节点

splitText(),此方法会将一个文本节点分成两个文本节点,即按照指定的位置分割nodeValue值。

function addNode(){ var element = document.createElement("div"); element.className = "message"; var textNode = document.createTextNode("Hello world!"); element.appendChild(textNode); document.body.appendChild(element); var newNode = element.firstChild.splitText(5); alert(element.firstChild.nodeValue); //"Hello" alert(newNode.nodeValue); //" world!" alert(element.childNodes.length); //2 }

Comment类型

nodeType的值为8nodeName的值为”#comment”;nodeValue的值是注释的内容;

parentNode可能是Document或Element

function getComment(){ var div = document.getElementById("myDiv"); var comment = div.firstChild; alert(comment.data); }

DocumentType类型

nodeType的值为10nodeName的值为doctype的名称;nodeValue的值null;parentNode是Document.

此对象的3个属性: * name:表示文档类型的名称 * entities:由文档类型描述的实体的NameNodeMap对象 * notations:由文档类型描述的符号的NameNodeMap对象

DocumentFragment类型

nodeType的值为11nodeName的值为”#document-fragment”;nodeValue的值null;parentNode是null.

虽然不能把文档片段直接添加到文档中,但可以作为一个“仓库”使用

function addItems(){ var fragment = document.createDocumentFragment(); var ul = document.getElementById("myList"); var li = null; for (var i=0; i < 3; i++){ li = document.createElement("li"); li.appendChild(document.createTextNode("Item " + (i+1))); fragment.appendChild(li); } ul.appendChild(fragment); }

Attr类型

元素的特性在DOM中以Attr类型表示。

nodeType的值为12nodeName的值为特性的名称;nodeValue的值是特性的值;parentNode是null.

Attr对象有3个属性: + name:特性名称 + value: 特性的值 + specified:布尔值,区别特性在代码中是指定的还是默认的

使用document.createAttribute()并传入特性的名称可以创建新的特性节点。

function assignAttribute(){ var element = document.getElementById("myDiv"); var attr = document.createAttribute("align"); attr.value = "left"; element.setAttributeNode(attr); alert(element.attributes["align"].value); //"left" alert(element.getAttributeNode("align").value); //"left" alert(element.getAttribute("align")); //"left" }

动态脚本

创建动态脚本有两种方法:

插入外部文件直接插入js代码

可以使用script元素的text属性来指定javascript代码:

function addScript(){ var script = document.createElement("script"); script.type = "text/javascript"; script.text = "function sayHi(){alert('hi');}"; document.body.appendChild(script); sayHi(); }

IE中,用到下面的方法:

function loadScriptString(code){ var script = document.createElement("script"); script.type = "text/javascript"; try { script.appendChild(document.createTextNode(code)); } catch (ex){ script.text = code; } document.body.appendChild(script); } function addScript(){ loadScriptString("function sayHi(){alert('hi');}"); sayHi(); }

动态样式

把css样式包含到html页面中的元素有2个:

link元素用于包含来自外部的文件

style元素用于指定嵌入的样式

function addStyle(){ var style = document.createElement("style"); style.type = "text/css"; style.appendChild(document.createTextNode("body{background-color:red}")); //error in IE var head = document.getElementsByTagName("head")[0]; head.appendChild(style); }

通用的解决方法:

function loadStyleString(css){ var style = document.createElement("style"); style.type = "text/css"; try{ style.appendChild(document.createTextNode(css)); } catch (ex){ style.styleSheet.cssText = css; } var head = document.getElementsByTagName("head")[0]; head.appendChild(style); } function addStyle(){ loadStyleString("body{background-color:red}"); }

操作表格

为table元素添加属性的方法:

caption:保存着对caption元素的指针tBodies:是一个tbody元素的HTMLCollectiontFoot:保存着对tfoot元素的指针tHead:保存着对thead元素的指针rows:是一个表格中所有行的HTMLCollectioncreateThead():创建thead元素,将其放到表格中,返回引用createTFoot():创建tfoot元素,将其放到表格中,返回引用createCaption():创建caption元素,将其放到表格中,返回引用deleteTHead():删除thead元素deleteTFoot():删除tfoot元素deleteCaption():删除caption元素deleteRow(pos):删除指定位置的行insertRow(pos):向rows集合中的指定位置插入一行

为tbody元素添加属性的方法:

rows:保存着tbody元素中行的HTMLCollectiondeleteRow(pos):删除指定位置的行insertRow(pos):向rows集合中的指定位置插入一行,返回对新插入行的引用

为tr元素添加属性的方法:

cells:保存着tr元素中单元格的HTMLCollectiondeleteCell(pos):删除指定位置的单元格insertCell(pos):向cells集合中的指定位置插入一个单元格,返回对新插入单元格的引用
转载请注明原文地址: https://www.6miu.com/read-19943.html

最新回复(0)