supce's blog

DOM(二)


这里主要记录下DOM基于CSS选择符和HTML5的一些扩展。

基于CSS选择符的扩展

有时候需要通过 CSS 选择符查询 DOM 文档取得元素的引用,可以使用以下两种方法:

  • querySelector()

    <div id="header">this is header</div>
    <p></p>
    <div class="footer">this is footer</div>
    <script type="text/javascript">
    console.log(document.querySelector("#header").firstChild.nodeValue);
    var text = document.createTextNode("this is a paragraph.");
    document.querySelector("p").appendChild(text);
    //获取类为footer的第一个元素
    document.body.querySelector(".footer");
    </script>
    

querySelector()方法接收一个CSS选择符,返回与该模式匹配的第一个元素,如果没有找到匹配的元素,返回null。

  • querySelectorAll()

    //取得某<div>中的所有<em>元素(类似于getElementsByTagName("em"))
    var ems = document.getElementById("myDiv").querySelectorAll("em");
    

querySelectorAll()方法接收的参数与querySelector()方法一样,都是一个CSS 选择符,但返回的是所有匹配的元素而不仅仅是一个元素。这个方法返回的是一个NodeList的实例。如果没有找到匹配的元素,NodeList就是空的。

要取得返回的NodeList中的每一个元素,可以使用item()方法,也可以使用方括号语法

  • matchesSelector()
    这个方法接收参数为CSS选择符,如果调用元素与该选择符匹配,返回true,否则,返回false。
    为了兼容其他浏览器,可以这样写:

    function matchesSelector(element, selector){
        if(element.matches){
            return element.matches(selector);
        } else if(element.matchesSelector){
            return element.matchesSelector(selector);
        } else if(element.webkitMatchesSelector){
            return element.webkitMatchesSelector(selector);
        } else if(element.msMatchesSelector){
            return element.msMatchesSelector(selector);
        } else if(element.mozMatchesSelector){
            return element.mozMatchesSelector(selector);
        } else if(element.oMatchesSelector){
            return element.oMatchesSelector(selector);
        }
    }
    

这个方法在做事件委托时可以用到,比如:

document.querySelector('#wrap').addEventListener('click',function(e){
    if(matchesSelector(e.target,'a.btn')){
        alert("do someting");
        console.log("do something");
        e.preventDefault();
        //TODO something
    }
    else{
        alert("...");
    }
},false);

HTML代码为:

<div id="wrap">
    <a class="btn" href="http://blog.supce.com">点我</a>
</div>

元素的遍历

对于元素间的空格,IE9及之前版本不会返回文本节点,而其他所有浏览器都会返回文本节点。之前写过一个删除空白文本节点的函数。为了解决元素访问的一致性问题,DOM也提供了一种方法:

  • childElementCount 返回不包括文本节点和注释的元素个数
  • firstElementChild 返回第一个子元素
  • lastElementChild 返回最后一个子元素
  • previousElementSibling 返回前一个同辈元素
  • nextElementSibling 返回后一个同辈元素

利用这些属性可以更方便的忽视空白元素进行元素的相关操作了。


基于HTML5的扩展

HTML5对DOM节点的相关操作进行了扩展

与类相关的扩展

为了更方便的利用JavaScript操作CSS类,比如动态修改类或者获取给定类的引用,HTML增加了一些API。

getElementsByClassName方法

getElementsByClassName()方法接收一个参数,即一个包含一或多个类名的字符串,返回带有指定类的所有元素的NodeList。传入多个类名时,类名的先后顺序不重要。比如:

var test_class = document.getElementsByClassName('test');
console.log(test_class); //[div.test, div.test.demo]
result = document.getElementsByClassName('test demo');
console.log(result); //[div.test.demo]

HTML代码为:

<div class="test">this is a test</div>
<div class="demo">this is a demo</div>
<div class="test demo">this is a test and demo</div>

classList属性

上面的方法可以根据类名获取元素的引用,而利用classList属性可以对类名进行增删与替换,该属性包含以下几种方法:

方法 描述
classList.remove() 删除元素中某个类名
classList.add() 在元素中添加某个类名
classList.toggle() 如果类名列表中已经存在给定的值,删除它;如果没有给定的值,添加它。
classList.contains() 表示类名列表中是否存在给定的值,如果存在则返回 true ,否则返回 false

举个改变div背景色的例子:

function changeColor(){
    var test = document.getElementById('test');
    test.classList.toggle('red');
    console.log("done");
}

HTML代码为:

<div id="test" class="red">this is a test</div>
<button id="btn" onclick="changeColor()">change color</button>

CSS代码为:

<style type="text/css">
    #test{
        width: 150px;
        height: 150px;
        line-height: 150px;
        text-align: center;
        color: white;
        background-color: green;
    }
    .red{
        background-color: red !important;
    }
</style>

考虑到兼容性问题,可以封装一个函数:

function classOperate(div,name,operate){
    var list = div.classList;
    if(list){
        switch(operate){
            case 'add':
                list.add(name);
                break;
            case 'remove':
                list.remove(name);
                break;
            case 'contains':
                return list.contains(name);
                break;
            case 'toggle':
                list.toggle(name);
                break;
        }
    }else{
        switch(operate){
            case 'add':
                addName(name);
                break;
            case 'remove':
                removeName(name);
                break;
            case 'contains':
                containName(name);
                break;
            case 'toggle':
                if(containName(name)){
                    removeName(name);
                }else{
                    addName(name);
                }
                break;
        }
    }
    function addName(name){
        div.className += (' ' + name);
    }
    function removeName(name){
        var classNames = div.className.split(/\s+/);
        for(var i=0;i<classNames.length;i++){
            if(classNames[i] == name){
                break;
            }
        }
        classNames.splice(i,1);
        div.className = classNames.join(' ');
    }
    function containName(name){
        var classNames = div.className.split(/\s+/);
        for(var i=0;i<classNames.length;i++){
            if(classNames[i] == name){
                return true;
            }
        }
        return false;
    }
}

焦点管理

HTML5增加了辅助管理DOM焦点的功能。

  • document.activeElement属性
    这个属性会引用DOM中当前获得了焦点的元素。比如:

    var btn = document.getElementById('btn');
    btn.focus();
    console.log(document.activeElement === btn);//true
    

默认情况下,文档刚刚加载完成时,document.activeElement 中保存的是 document.body 元素的引用。文档加载期间,document.activeElement的值为 null 。

  • document.hasFocus()方法

这个方法用于确定文档是否获得了焦点。比如:

var btn = document.getElementById('btn');
btn.focus();
console.log(document.hasFocus());//true

HTMLDocument的变化

HTML5 同时也扩展了 HTMLDocument ,增加了一些新的功能

readyState属性

Document 的 readyState 属性有两个可能的值:

属性值 描述
loading 正在加载文档
complete 已经加载完文档

一般会这样使用:

if(document.readyState == 'complete'){
    //do something
}

兼容模式

自从IE6开始区分渲染页面的模式是标准的还是混杂的,检测页面的兼容模式就成为浏览器的必要功能。为此给 document 添加了一个名为 compatMode的属性,这个属性就是为了告诉开发人员浏览器采用了哪种渲染模式在标准模式下, document.compatMode 的值等于 “CSS1Compat” ,而在混杂模式下,document.compatMode 的值等于 “BackCompat” 。

if(document.compatMode == "CSS1Compat"){
    alert("Standards mode");
}else{
    alert("Quirks mode");
}

head属性

HTML5 新增了 document.head 属性

var head = document.head || document.getElementsByTagName('head')[0];

字符集属性

HTML5 新增了几个与文档字符集有关的属性

  • charset属性

charset 属性表示文档中实际使用的字符集,也可以用来指定新字符集。

  • defaultCharset属性

该属性表示根据默认浏览器及操作系统的设置,当前文档默认的字符集应该是什么。

如果文档没有使用默认字符集,charset和defaultCharset属性的值可能会不一样

自定义数据属性

HTML5规定可以为元素添加非标准的属性,但要添加前缀 data-,比如:

<div id="test" data-myname="demo">this is a test</div>

同时可以获取或者设置自定义数据的值

var test = document.getElementById('test');
console.log(test.dataset.myname);//demo
test.dataset.myname = "newName";
console.log(test.dataset.myname);//newName

插入标记

有时候可能需要向文档中插入大量新的HTML标记,如果使用DOM创建一系列节点,并按正确顺序连接起来还是比较麻烦的。而使用下面几个属性和方法可以更简单,迅速的将标记插入到文本中。

innerHTML属性

在读模式下,innerHTML属性返回与调用元素的所有子节点(包括元素、注释和文本节点)对应的 HTML 标记。比如:

<div id="test">
    <p>this is a test</p>
    <p>this is a demo</p>
</div>
var test = document.getElementById('test');
console.log(test.innerHTML);


在写模式下,innerHTML的值会被解析为DOM子树,替换调用元素原来的所有子节点。因为它的值被认为是HTML,所以其中的所有标签都会按照浏览器处理HTML的标准方式转换为元素。如果设置的值仅是文本而没有 HTML标签,那么结果就是设置纯文本。比如:

<div id="test">
    <p>this is a test</p>
    <p>this is a demo</p>
</div>
var test = document.getElementById('test');
console.log(test.innerHTML);
test.innerHTML = "Hello & welcome <br /> this is a test!";
console.log(test.innerHTML);

outerHTML属性

在读模式下,outerHTML返回调用它的元素及所有子节点的HTML标签。比如:

<div id="test">
    <p>this is a test</p>
    <p>this is a demo</p>
</div>
var test = document.getElementById('test');
console.log(test.outerHTML);


在写模式下, outerHTML会根据指定的 HTML 字符串创建新的 DOM子树,然后用这个 DOM 子树完全替换调用元素。比如:
使用 outerHTML 属性以下面这种方式设置值:
div.outerHTML = "<p>This is a paragraph.</p>";
这行代码完成的操作与下面这些 DOM 脚本代码一样:

var p = document.createElement("p");
p.appendChild(document.createTextNode("This is a paragraph."));
div.parentNode.replaceChild(p, div);

结果,就是新创建的<p>元素会取代DOM树中的<div>元素。

insertAdjacentHTML()方法

该接收两个参数:插入位置和要插入的 HTML 文本。第一个参数必须是下列值之一:

属性值 描述
beforebegin 在当前元素之前插入一个紧邻的同辈元素
afterbegin 在当前元素之下插入一个新的子元素或在第一个子元素之前再插入新的子元素
beforeend 在当前元素之下插入一个新的子元素或在最后一个子元素之后再插入新的子元素
afterend 在当前元素之后插入一个紧邻的同辈元素

注意,这些值都必须是小写形式

第二个参数是一个HTML字符串,如果浏览器无法解析该字符串,就会抛出错误。比如:

//作为前一个同辈元素插入
element.insertAdjacentHTML("beforebegin", "<p>Hello world!</p>");

scrollIntoView方法

scrollIntoView()方法,通过滚动浏览器窗口或容器元素,以便在当前视窗的可见范围看见当前元素。如果参数为true,或者省略它,窗口会尽可能滚动到自身顶部与元素顶部平齐。
可以写一个导航栏固定,点击导航栏滚动到相应div的小例子:

HTML代码为:

<div id="header">
    <div id="nav">
        <ul class="floatFix">
            <li class="red">第一部分</li>
            <li>第二部分</li>
            <li>第三部分</li>
        </ul>
    </div>
</div>
<div id="first_section" class="section">
    <p>第一部分</p>
</div>
<div id="sec_section" class="section">
    <p>第二部分</p>
</div>
<div id="third_section" class="section">
    <p>第三部分</p>
</div>

CSS代码为:

<style type="text/css">
*{
    margin: 0;
    padding: 0;
}
#header{
    width: 100%;
    height: 50px;
    position: fixed;        
    top: 0;
    background-color: rgb(83,83,83);
}
#nav{
    margin: auto auto;
    width: 60%;
}
#nav ul{
    list-style: none;
    cursor: pointer;
}
#nav li{
    float: left;
    width: 30%;
    height: 50px;
    line-height: 50px;
    text-align: center;
    color: white;
}
.floatFix:after{
    clear: both;
    display: block;
    visibility: hidden;
    content: "";
    height: 0;
    ;line-height: 0;
}
.section{
    width: 100%;
    text-align: center;
    color: white;
}
#first_section{
    background-color: red;
}
#sec_section{
    background-color: green;
}
#third_section{
background-color: yellow;
}
.red{
    background-color: red;
}
.green{
    background-color: green;
}
.yellow{
    background-color: yellow;
}
</style>

JavaScript代码:

<script type="text/javascript">
    var $ = function(id){
        return document.getElementById(id);
    }
    var client_height = screen.availHeight;
    var array = [$('first_section'),$('sec_section'),$('third_section')];
    for(var i=0;i<3;i++){
        console.log(array[i]);
        array[i].style.height = (client_height - 50) + "px";
        array[i].style.lineHeight = (client_height - 50) + "px";
    }
    var li = document.getElementsByTagName('li');
    li[0].onclick = function(){
        li[0].className = "red";
        li[1].className = " ";
        li[2].className = " ";
        $('first_section').scrollIntoView(false);
    }
    li[1].onclick = function(){
        li[0].className = " ";
        li[1].className = "green";
        li[2].className = " ";
        $('sec_section').scrollIntoView(false);
    }
    li[2].onclick = function(){
        li[0].className = " ";
        li[1].className = " ";
        li[2].className = "yellow";
        $('third_section').scrollIntoView(false);
    }
</script>

效果如下:

文章导图