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

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">&lt;script&gt;alert("Hi")&lt;/script&gt;</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值为1
  • nodeName的值为元素的标签名
  • 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的值为3
  • nodeName的值为”#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的值为8
  • nodeName的值为”#comment”;
  • nodeValue的值是注释的内容;
  • parentNode可能是Document或Element

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

DocumentType类型

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

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

DocumentFragment类型

  • nodeType的值为11
  • nodeName的值为”#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的值为12
  • nodeName的值为特性的名称;
  • 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元素的HTMLCollection
  • tFoot:保存着对tfoot元素的指针
  • tHead:保存着对thead元素的指针
  • rows:是一个表格中所有行的HTMLCollection
  • createThead():创建thead元素,将其放到表格中,返回引用
  • createTFoot():创建tfoot元素,将其放到表格中,返回引用
  • createCaption():创建caption元素,将其放到表格中,返回引用
  • deleteTHead():删除thead元素
  • deleteTFoot():删除tfoot元素
  • deleteCaption():删除caption元素
  • deleteRow(pos):删除指定位置的行
  • insertRow(pos):向rows集合中的指定位置插入一行

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

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

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

  • cells:保存着tr元素中单元格的HTMLCollection
  • deleteCell(pos):删除指定位置的单元格
  • insertCell(pos):向cells集合中的指定位置插入一个单元格,返回对新插入单元格的引用

猜你喜欢

转载自blog.csdn.net/Wxy971122/article/details/76713449
今日推荐