零js基礎 | 程序設計 | 11 - DOM扩展

DOM扩展

    var div = document.querySelector('#div')

    //返回NodeList實例
    var div = document.querySelectorAll('.divClass')

    console.log(div.item(0))
    console.log(div[0])
    console.log(div)

matchesSelector(CSS選擇符),如果匹配 true,否則 false

if (matchesSelector(div[0], '#div.divClass')) {
    alert(true)
}
//兼容性
function matchesSelector(element, selector) {
    if (element.matchesSelector) {
        return element.matchesSelector(selector);
    } else if (element.msMatchesSelector) {
        return element.msMatchesSelector(selector);
    } else if (element.mozMatchesSelector) {
        return element.mozMatchesSelector(selector);
    } else if (element.webkitMatchesSelector) {
        return element.webkitMatchesSelector(selector);
    } else {
        throw new Error("Not supported.");
    }
}

支持Element Traversal 规范的浏览器有IE 9+、Firefox 3.5+、Safari 4+、Chrome 和Opera 10+。

 childElementCount:返回子元素(不包括文本节点和注释)的个数。
 firstElementChild:指向第一个子元素;firstChild 的元素版。
 lastElementChild:指向最后一个子元素;lastChild 的元素版。
 previousElementSibling:指向前一个同辈元素;previousSibling 的元素版。
 nextElementSibling:指向后一个同辈元素;nextSibling 的元素版。

二、困扰你我的NodeList与HTMLCollection      
  相同点:

    1. 类数组。有length属性,可以用下标索引来访问其中的元素,但没有Array的slice等方法;

    2. 只读。无法增删其中的元素;

    3. 实时同步DOM树的变化。若DOM树有新元素加入,该类型的对象也会将新元素包含进来;

    4. 可通过下标数字类型索引获取集合中指定位置的元素;

    5. 可通过item({String | Number} 索引)方法获取集合中指定位置的元素,若通过索引找不到元素,则以第一个元素作为返回值。

  不同点(主要表现在HTMLCollection比NodeList能力更强大):

    1. HTMLCollection对象可通过namedItem({String} id或name)获取首个匹配的元素,若没有则返回null;

    2. HTMLCollection对象可通过点方式获取第个id或name匹配的元素,若没有则返回undefined。

  各浏览器选择器返回类型差别:

        
        console.log(div instanceof NodeList)  //true

        //帶有.divClass.username的元素
        var div = document.getElementsByClassName('divClass username')
        console.log(div)

        console.log(div instanceof HTMLCollection) //true;

傳統刪class

        var classNames = div.className.split(/\s+/);
        var pos = -1;
        
        for(var i=0;i<classNames.length;i++){
            if(classNames[i] == 'user'){
                pos = i;
                break;
            }
        }
        classNames.splice(i,1);
        div.className = classNames.join(' ');

classList,IE10+,chrome,firefox

        div[0].classList.remove('username')
        div[0].classList.add('current')
        div[0].classList.toggle('divClass')
        
        
        if(div[0].classList.contains('password') && div[0].classList.contains('disabled')){
            alert(true)
        }

焦点管理

var btn = document.getElementById('myButton')
btn.focus()
alert(document.activeElement == btn) //true 

//文档加载完成时,document.activeElement保存的是document.body引用

//确定文档是否获得了焦点
alert(document.hasFocus()) //true

HTMLDocument的变化
1、readyState 两个值 loading:正在加载;complete:已经完成

if(document.readyState == 'complete'){
    
}

2、document.compatMode 的
值等于"CSS1Compat",而在混杂模式下,document.compatMode 的值等于"BackCompat"。

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

3、head属性
Chrome 和Safari 5。

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

字符集属性

alert(document.charset); //"UTF-16"
document.charset = "UTF-8";

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

if (document.charset != document.defaultCharset){
alert("Custom character set being used.");
}

自定义数据属性
data-,通过dataset访问 ,dataset是DOMStringMap的一个实例,每个data-name形式的属性都会有一个对应的属性,
比如,自定义属性是data-myname,
那映射中对应的属性就是myname)

<div id="myDiv" data-appId="12345" data-myname="Nicholas"></div>
        var div = document.getElementById('myDiv')
        
        //获取
        var appId = div.dataset.appId;
        var myName = div.dataset.myname;
        //设置
        div.dataset.appId = 123456
        div.dataset.myname = 'elmok'
        
        if(div.dataset.myname){
            alert('hello ' + div.dataset.myname)
        }

插入标记

innerHTML不同浏览器返回的值大小写可能不同
outerHTML返回包括外层
insertAdjacentHTML()两个参数
 "beforebegin",在当前元素之前插入一个紧邻的同辈元素;
 "afterbegin",在当前元素之下插入一个新的子元素或在第一个子元素之前再插入新的子元素;
 "beforeend",在当前元素之下插入一个新的子元素或在最后一个子元素之后再插入新的子元素;
 "afterend",在当前元素之后插入一个紧邻的同辈元素。

<div id="content"></div>
    //作为前一个同辈元素插入
    con.insertAdjacentHTML("beforebegin", "<p>beforebegin!</p>");
    //作为第一个子元素插入
    con.insertAdjacentHTML("afterbegin", "<p>afterbegin!</p>");
    //作为最后一个子元素插入
    con.insertAdjacentHTML("beforeend", "<p>beforeend!</p>");
    //作为后一个同辈元素插入
    con.insertAdjacentHTML("afterend", "<p>afterend!</p>");

之后

<p>beforebegin!</p>
<div id="content">
    <p>afterbegin!</p>
    <p>beforeend!</p>
</div>
<p>afterend!</p>

在使用innerHTML、
outerHTML 属性和insertAdjacentHTML()方法时,最好先手工删除要被替换的元素的所有事件处理
程序和JavaScript 对象属性

for (var i=0, len=values.length; i < len; i++){
ul.innerHTML += "<li>" + values[i] + "</li>"; //要避免这种频繁操作!!
}

//正确做法
var itemsHtml = "";
...

scrollIntoView(),滚动标准作法
如果给这个方法传入true 作为参数,或者不传入任何参数,那么窗口滚动之后会让调用元素的顶部与视口顶部尽可能平齐;
如果传入false 作为参数,调用元素会尽可能全部
出现在视口中,(可能的话,调用元素的底部会与视口顶部平齐。)不过顶部不一定平齐

只有调用才会出现在视口中,类似于a标签本页定位的效果

var btn = document.getElementById('btn')
var div = document.getElementById('content')

btn.onclick = function(){
div.scrollIntoView(true)
}

IE “文档模式”(document mode) 文档模式决定了你可以使用哪个级别的 CSS
要强制浏览器以某种模式渲染页面,可以使用 HTTP头部信息 X-UA-Compatible,或通过等价的 <meta>标签来设置:

//<meta http-equiv="X-UA-Compatible" content="IE=IEVersion"> 

<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7">

    var mode = document.documentMode; 
    alert(mode) // 11/10/9/8/7/5....  

children属性
。IE8及更早版本的 children 属性中也会包含注释节点,但 IE9之后的版本则 只返回元素节点

contains()方法.
经常需要知道某个节点是不是另一个节点的后代

alert(document.documentElement.contains(document.body)); //true
  1. innerText 属性
    outerText 属性 //包括父级也替换

滚动

scrollIntoView()
scrollIntoViewIfNeeded(alignCenter):只在当前元素在视口中不可见的情况下,才滚
动浏览器窗口或容器元素,最终让它可见。如果当前元素在视口中可见,这个方法什么也不做。
如果将可选的alignCenter 参数设置为true,则表示尽量将元素显示在视口中部(垂直方向)。
Safari 和Chrome 实现了这个方法。
 scrollByLines(lineCount):将元素的内容滚动指定的行高,lineCount 值可以是正值,
也可以是负值。Safari 和Chrome 实现了这个方法。
 scrollByPages(pageCount):将元素的内容滚动指定的页面高度,具体高度由元素的高度决
定。Safari 和Chrome 实现了这个方法。
希望大家要注意的是,scrollIntoView()和scrollIntoViewIfNeeded()的作用对象是元素的
容器,而scrollByLines()和scrollByPages()影响的则是元素自身。下面还是来看几个示例吧。

//将页面主体滚动5 行
document.body.scrollByLines(5);
//在当前元素不可见的时候,让它进入浏览器的视口
document.images[0].scrollIntoViewIfNeeded();
//将页面主体往回滚动1 页
document.body.scrollByPages(-1);

由于scrollIntoView()是唯一一个所有浏览器都支持的方法,因此还是这个方法最常用。

零js基礎 | 程序設計 | 10 - DOM

Node.nodeType == 1 元素节点
Node.nodeType == 2 属性节点
Node.nodeType == 3 文本节点

<head>
    <meta charset="UTF-8">
    <title></title>
</head>

    var h = document.getElementsByTagName('head')[0]
 //   h.nodeType == 1 && console.log('this is an elem')
    h.nodeType == 1 && console.log(h.nodeName) //HEAD

//每个节点都有一个childNodes 属性,其中保存着一个NodeList 对象。即类数组对象
    console.log(h.childNodes[0])
    console.log(h.childNodes[1])
    console.log(h.childNodes[2])
    console.log(h.childNodes[3])
    console.log(h.childNodes[4])

    console.log(h.parentNode)  //5

    console.log(h.childNodes.length)

把NodeTypelist转为数组

function convertToArray(nodes) {
    var array = null;
    try {
        array = Array.prototype.slice.call(nodes, 0); //针对非IE 浏览器
    } catch (ex) {
        array = new Array();
        for (var i = 0, len = nodes.length; i < len; i++) {
            array.push(nodes[i]);
        }
    }
    return array;
}

每个Node都有一个parentNode属性,该属性指向文档树中的父节点;
nodeName //HEAD
parentNode //Html文档结构
childNodes //Nodelists节点集合
previousSibling //前一个
nextSibling //后一个

如果没有子节点,那么firstChild 和lastChild 的值均为null

hasChildNodes(),这个方法在节点包含一或多个子节点的情况下返回true;应该说,这是比查询childNodes
列表的length 属性更简单的方法。

h.hasChildNodes()

ownerDocument,所有节点的共同属性,指向整个文档节点

console.log(h.ownerDocument)

操作节点

末尾:
appendChild()相当于向childNodes列表尾部添加一个节点,,如果是已有节点,则相当于剪切;

中间:
insertBefore(),向childNodes列表放在特定的位置上
insertBefore(要插入的节点,作为参照的节点) 【插入之后按顺序前后排:要插入的节点,作为参照的节点】

插入节点后,被插
入的节点会变成参照节点的前一个同胞节点(previousSibling),

returnedNode = someNode.insertBefore(newNode, null);
alert(newNode == someNode.lastChild); //true

//插入后成为第一个子节点
var returnedNode = someNode.insertBefore(newNode, someNode.firstChild);
alert(returnedNode == newNode); //true
alert(newNode == someNode.firstChild); //true

//插入到最后一个子节点前面
returnedNode = someNode.insertBefore(newNode, someNode.lastChild);
alert(newNode == someNode.childNodes[someNode.childNodes.length-2]); //true

替换:

replaceChild(要插入的节点,要替换的节点)

//替换第一个子节点
var returnedNode = someNode.replaceChild(newNode, someNode.firstChild);

//替换最后一个子节点
returnedNode = someNode.replaceChild(newNode, someNode.lastChild);

移除:
removeChild(要移除的节点)

前面介绍的四个方法操作的都是某个节点的子节点,也就是说,要使用这几个方法必须先取得父节
点(使用parentNode 属性)。另外,并不是所有类型的节点都有子节点,如果在不支持子节点的节点
上调用了这些方法,将会导致错误发生。

共有的节点方法:

cloneNode(true/false),需要再使用 appendChild()/insertBefore()/replaceChild()

normalize() /这个方法唯一的作用就是处理文档树中的文本节点



Document类型

 nodeType 的值为9;
 nodeName 的值为"#document";
 nodeValue 的值为null;
 parentNode 的值为null;
 ownerDocument 的值为 null;
Document 节点的子节点可以是DocumentType、Element、ProcessingInstruction
或Comment,

内置访问子节点的快捷方式:
document.documentElement属性

document.documentElement ==
document.childNodes[0] ==
document.firstChild ==

document.body 属性,直接指向<body>元素;

另一个子节点可能是:(浏览器支持有限)

var doctype = document.doctype; //取得对<!DOCTYPE>的引用

文档信息

document对象属性:

//取得文档标题
var originalTitle = document.title;

//设置文档标题
document.title = "New page title";

//取得完整的URL
var url = document.URL;

//取得域名
var domain = document.domain;

//取得来源页面的URL
var referrer = document.referrer;

IE7下,name與ID相同的不同元素使用doc..ById會返回name的元素

    alert(document.getElementById('myelem').nodeName)

不要讓表單字段的name與其他ID相同

var imgs = document.getElementsByTagName('img')
var imgsname = imgs.namedItem('myimgName')

取得單選按鈕【常用】 getElementByName('color');

特殊集合
document.anchors:包括name屬性的a元素
document.links:包含所有帶href特性的a元素
document.images:包含所img元素
document.forms:包含所form元素

一個element類型:

    console.log(box.nodeType)  //1
    console.log(box.nodeName)  //DIV
    console.log(box.nodeValue) //null
    
    console.log(box.parentNode) //<body......body>
    console.log(box.childNodes) //[elem..text..com...]
    
    console.log(box.tagName) //DIV  tagName == nodeName

HTML元素,可读可写

alert(div.id); //"myDiv""
alert(div.className); //"bd"
alert(div.title); //"Body text"
alert(div.lang); //"en"
alert(div.dir); //"ltr"

取得特性

alert(div.getAttribute("id")); //"myDiv"
alert(div.getAttribute("class")); //"bd"
alert(div.getAttribute("title")); //"Body text"
alert(div.getAttribute("lang")); //"en"
alert(div.getAttribute("dir")); //"ltr"

不存在返回null;

自定义属性应该加上data-前缀以便验证

    var div = document.getElementById('myDiv')
        alert(div.my_special_attribute); //undefined(IE<8 弹 hello ,其它弹undefined)

设置属性:
box.setAttribute('style') //如果不写出错 box.setAttribute('style','')//正确

自定义属性不能简单地使用.来设置

        box.mycolor = 'red';  //使用.设置
        alert(box.getAttribute('mycolor')) //null;(IE9<可弹出)
        
        //setAttributte配套使用;
        box.setAttribute('myspace','300px')
        alert(box.getAttribute('myspace'))

div.removeAttribute("class");完全清除,彻底删除元素特性;

  1. attributes 属性
    Element 类型是使用attributes 属性的唯一一个DOM 节点类型。attributes 属性中包含一个

NamedNodeMap,与NodeList 类似,也是一个“动态”的集合。元素的每一个特性都由一个Attr 节
点表示,每个节点都保存在NamedNodeMap 对象中。NamedNodeMap 对象拥有下列方法

遍历元素特性序列化

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;
        if (element.attributes[i].specified) {
            pairs.push(attrName + "=\"" + attrValue + "\"");
        }
    }
    return pairs.join(" ");
}

console.log(outputAttributes(box)) // id="box" style="font-size:14px;color:#000" class="boxclass"

创建元素

``javascript

var div = document.body.appendChild('div')
div.id = mydiv
document.body.appendChild(div)

创建文本

var box = document.getElementById('box')
if(box.childNodes[0].nodeType == 3 ){
    alert(123)
}
alert(box.childNodes[0].nodeValue)
alert(box.childNodes[0].data)

Text类型
var e = document.createElement('div')
e.className = 'message'
var atnode = document.createTextNode('fuck ')
e.appendChild(atnode)

var atnode2 = document.createTextNode('world')
e.appendChild(atnode2)


alert(e.childNodes.length) //2  个节点 

e.normalize()  //规范化成1个(合并)
alert(e.childNodes.length) //1

alert(e.firstChild.nodeValue) //fuck world

分割  splitText()
var e = document.createElement('div')
e.className = 'message'

var Tnode = document.createTextNode('hello world')
e.appendChild(Tnode)
document.body.appendChild(e);
var newNode = e.firstChild.splitText(2)
alert(e.firstChild.nodeValue) //he 从2的位置开始分割
comment类型 注释类型 nodeType == 8 
CDATASection类型 XML 文档,表示CDATA区域 nodeType == 4 
DocumentType类型 nodeType == 10
DocumentFragment类型 nodeType == 11;
Attr类型 存在于元素的attributes属性中的节点,nodeType == 11; ??

创建动态脚本:

function loadScript(url) {

var script = document.createElement('script')
script.type = 'text/javascript'
script.src = url
document.body.appendChild(script)

}
loadScript('main.js')


代码执行
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)
}
loadScriptString("function sayHi(){alert('hi')}")`
其实效果跟     eval("function sayName(){alert('name')}")  一样; 

动态样式
function loadStyles(url) {
    var link = document.createElement('link')
    link.rel = 'stylesheet'
    link.type = 'text/css'
    link.href = url
    var head = document.getElementsByTagName('head')[0]
    head.appendChild(link)
}
loadStyles('style.css')

function loadStyleString(css) {

var style = document.createElement('style')
style.type = 'text/css'
try {
    style.appendChild(document.createTextNode(css))
} catch (ex) {
    style.style.cssText = css;
}
document.getElementsByTagName('head')[0].appendChild(style);

}

loadStyleString('body{background:#1F78BF}')

操作表格
cell 11 cell 22
cell 33 cell 44
var table = document.createElement('table')
table.border = 1;
table.width = '100%'

//tbody

var tbody = document.createElement('tbody')
table.appendChild(tbody)

//第一行

tbody.insertRow(0)
tbody.rows[0].insertCell(0);
tbody.rows[0].cells[0].appendChild(document.createTextNode('cell 11'))
tbody.rows[0].insertCell(1);
tbody.rows[0].cells[1].appendChild(document.createTextNode('cell 22'))

//第二行

tbody.insertRow(1)
tbody.rows[1].insertCell(0);
tbody.rows[1].cells[0].appendChild(document.createTextNode('cell 33'))
tbody.rows[1].insertCell(1);
tbody.rows[1].cells[1].appendChild(document.createTextNode('cell 44'))

document.body.appendChild(table)

**HTMLCollection、NodeList以及NamedNodeMap这三个集合都是“动态的”,每当文档发生变化时,他们都会更新。他们将始终保持这最新、最准确的消息。且看以下程序:**
var divs = document.getElementsByTagName('div'),
    i, div;
for (i = 0; i < divs.length; i++) {
    div = document.createElement('div')
    document.body.appendChild(div)
}
//以上代码,第一次取得所有div 元素的HTMLCollection,下次取得会更新,造成死循环

//正确使用

var divs = document.getElementsByTagName('div'),
    i, len, div;
for (i = 0, len = divs.length; i < len; i++) {
    div = document.createElement('div')
    document.body.appendChild(div)
}

<hr />

DOM元素的特性(Attribute)  和    属性(Property)

Attribute特性: dom节点自带的属性,例如html中常用的id、class、title、align等:

//赋值:之后立即体现到DOM ,如果是标准特性,也会关联到相对应的属性值 ;

div1.setAttribute('class', 'a');
div1.setAttribute('title1', 'c');
Property属性: 而Property是这个DOM元素作为对象,其附加的内容,例如childNodes、firstChild等:

//取值,用.
var id = div1.id;
var attrs = div1.attributes;

//上面代码中的div1.attributes是取的attributes这一属性,取出来保存到attrs变量中,attrs就成了一个NamedNodeList类型的对象,里面存储了若干个Attr类型。

//赋值 :对属性Property可以赋任何类型的值,而对特性Attribute只能赋值字符串!
div1.className = 'a';



<hr >
NodeList v.s. HTMLCollection

Node及对应集合NodeList
Element(继承Node)及对应集合HTMLCollection
Document(继承Node)