网站首页 > 知识剖析 正文
本内容是《Web前端开发之Javascript视频》的课件,请配合大师哥《Javascript》视频课程学习。
DOM分级及模块:
DOM1级主要定义的是HTML和Xml文档的底层结构;DOM2和DOM3则在此结构的基础上引入了更多的交互能力,也支持了更高级的XML特性;DOM2和DOM3级分为许多模块(模块之间具有某种关联),分别描述了DOM的某个非常具体的子集;
DOM2和DOM3的目的在于扩展DOM API,以满足操作XML的所有需求,同时提供更好的错误处理及特性检测能力;模块如下:
- DOM2级核心(DOM Level 2 Core):并没有引入新类型,只是在DOM1级的基础上通过更新或增加新方法和新属性来增强即有的类型;
- DOM2级视图(DOM Level 2 Views):定义了跟踪不同文档视图的接口;
- DOM2级事件(DOM Level 2 Events):定义了如何使用事件与DOM文档交互;
- DOM2级样式(DOM Level 2 Style):定义了如何以编程方式来访问和改变CSS样式;
- DOM2级遍历和范围(DOM Level 2 Traversal):引入了遍历DOM文档和选择其特定部分的新接口;
- DOM2 范围(DOM 2 Range):允许创建、插入、修改和删除同一个Document、DocumentFragment或者 Attr类型节点中的内容;
- DOM2级HTML(DOM Level 2 HTML):在1级HTML基础上构建,添加了更多属性、方法和新接口;
DOM3级(DOM Level 3):
- DOM3 核心(The DOM3 Core):继承了DOM1和DOM2 核心(Core) ,增强了即有类型,但也引入了一些新类型;增加了新的方法和属性,如:adoptNode()和textContent等;
- DOM3 Load and Save文档加载和保存:允许程序和脚本动态地加载XML文档的内容到DOM文档中,序列化DOM文档到XML文档中;
- DOM3 Validation(DOM 3有效性验证):当能够确保文档保持有效的时候,才可允许程序和脚本动态地更新内容和结构化文档;
- DOM3 Events:基于DOM2 Events的扩展;
- DOM3 XPath:提供了简单的API用于访问使用XPath 1.0版本的DOM树;
DOM一致性检测:
由于DOM分为多个级别,也包含多个部分,因此检测浏览器实现了DOM的哪些部分就非常有必要;
document.implementation属性:会返回一个和当前文档相关联的DOMImplementation对象;DOMImplementation接口代表了一个对象,这个对象提供了不依赖于任何document的方法;这个对象可以通过document.implementation属性获得;
console.log(document.implementation); // DOMImplementation {}
这个接口没有特定的属性,父类就是Object,因此也没有继承到任何属性和方法;
DOM1级只为DOMImplementation对象规定了一个方法hasFeature(),它是检测浏览器是否支持某个DOM模块,其返回一个布尔值,该方法接受两个参数:要检测的DOM模块的名称及版本号;
console.log(document.implementation);
console.log(document.implementation.hasFeature("XML","1.0"));
- 功能 版本号 说明
- Core 1.0 2.0 3.0 基本的DOM,用于描述表现文档的节点树
- XML 1.0 2.0 3.0 Core的XML扩展,添加对CDATA,处理指令及实体的支持
- HTML 1.0 2.0 XML的HTML扩展,添加了对HTML特有元素及实体的支持
- Views 2.0 基于某些样式完成文档的格式化
- StyleSheets 2.0 将样式表关联到文档
- CSS 2.0 对层叠样式表1级支持
- CSS2 2.0 对层叠样式表2级支持
- Events 2.0 常规的DOM事件
- UIEvents 2.0 用户界面事件
- MouseEvents 2.0 由鼠标引发的事件
- MutationEvents 2.0 DOM树变化时引发的事件
- HTMLEvents 2.0 HTML4.01事件
- Range 2.0 用于操作DOM树中某个范围的对象和方法
- Traversal 2.0 遍历DOM树的方法
- LS 3.0 文件与DOM树之间的同步加载与保存
- LS-Async 3.0 文件与DOM树之间的异步加载与保存
- Validation 3.0 在确保有效的前提下修改DOM树的方法
可以检测是否支持某些DOM模块,如:
var supportsDOM2Core = document.implementation.hasFeature("Core","2.0");
var supportsDOM3Core = document.implementation.hasFeature("Core","3.0");
var supportsDOM2HTML = document.implementation.hasFeature("HTML","2.0");
var supportsDOM2Views = document.implementation.hasFeature("Views","2.0");
var supportsDOM2XML = document.implementation.hasFeature("XML","2.0");
var supportsDOM3Validation = document.implementation.hasFeature("Validation","3.0");
根据官方的说明,这个方法并不是十分的可靠,虽然有时返回 true,但并不意味着实现与规范一致,如Safari2.0及更早版本会在没有完全实现某些DOM功能的情况下也返回true;所以,最好的做法是,除了检测hasFeature()之外,同时使用能力检测;
DOM变化:
1.DocumentType类型的变化:
新增了3个属性:publicId、systemId和internalSubset;其中,前两个属性表示是文档类型声明中的两个信息段,这两个信息段在DOM1级中是没有办法访问到的,如:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
对于该文档类型声明,publicId是”-//W3C//DTD HTML 4.01//EN”;而systemId是http://www.w3.org/TR/html4/strict.dtd;但是对于HTML5文档,这两个属性返回空字符串;
console.log(document.doctype.publicId);
console.log(document.doctype.systemId);
internalSubset用于访问包含在文档类型声明中的额外定义,如:访问document.doctype.internalSubset将得到<!ELEMENT name (#PCDATA)>
<!DOCTYPE HTML PUBLIC ”-//W3C//DTD HTML 4.01//EN” http://www.w3.org/TR/html4/strict.dtd
[<!ELEMENT name #PCDATA(>]>
alert(document.doctype.internalSubset);
2.框架的变化:
框架是HTMLIFrameElement对象,它在DOM2级中都有一个新属性contentDocument;该属性包含一个指针,指向表示框架内容的文档对象;
var iframe = document.getElementById("myIframe");
var iframeDoc = iframe.contentDocument;
console.log(iframeDoc); // #document
由于contentDocument是Document类型的实例,因此可以像使用其他HTML文档一样使用,包括所有属性和方法;
几乎所有的浏览器都支持该属性;但IE8之前不支持,但支持一个名叫contentWindow的属性,该属性返回框架的window对象,而这个window对象又有一个document属性;所有浏览器都支持contentWindow属性;
console.log(iframe.contentWindow.document); // #document
// 兼容写法
var iframeDoc = iframe.contentDocument || iframe.contentWindow.document;
console.log(iframeDoc);
访问框架或内嵌框架的文档对象要受到跨域安全策略的限制;如果某个框架中的页面来自其他域或不同子域,或者使用了不同的协议,那么要访问这个框架的文档对象就会导致错误;
3.Document类型的变化:
document.importNode(externalNode, deep)方法:
将外部文档的一个节点拷贝一份,然后可以把这个拷贝的节点插入到当前文档中,而源节点也不会从外部文档中被删除;
参数:externalNode将要从外部文档导入的节点,deep参数可选,为一个布尔值,表明是否要导入节点的后代节点(深度克隆),其与Element的cloneNode()非常类似;
该方法返回一个新节点.,新节点的parentNode是null,因为它还没有插入当前文档的文档树中,属于游离状态,所以必须使用相关的方法把它添加到当前文档中;
<iframe src="iframe.html"></iframe>
<div id="mydiv"></div>
<script>
window.onload = function(){
var iframe = document.getElementsByTagName("iframe")[0];
var myNode = iframe.contentDocument.getElementById("myNode");
var newNode = document.importNode(myNode, true);
console.log(newNode);
console.log(newNode === myNode); // false
console.log(newNode.ownerDocument); // 所属文档并不同
console.log(myNode.ownerDocument);
document.getElementById("mydiv").appendChild(newNode);
}
</script>
每个节点都有一个ownerDocument属性,如果调用appendChild()时传入的节点属于不同的文档,会导致错误,但在调用importNode()时传入不同文档的节点则会返回一个新节点,该节点的所有权归当前文档所有;
document.adoptNode(externalNode)方法:
该方法的作用是,从其他的document文档中获取一个节点,该节点以及它的子树上的所有节点都会从原文档删除 , 并且它的ownerDocument属性会变成当前的document文档,随后可以把这个节点插入到当前文档中;
该方法只有一个参数externalNode,即将要从外部文档导入的节点;
<iframe src="iframe.html"></iframe>
<button id="btn">移动元素</button>
<script>
function getEle(){
var iframe = document.getElementsByTagName("iframe")[0];
var elt = iframe.contentDocument.body.firstElementChild;
if(elt)
document.getElementById("mydiv").appendChild(document.adoptNode(elt));
else
alert("移动完了");
}
document.getElementById("btn").onclick = getEle;
</script>
该方法不但可以从iframe中获取adopt元素,在同一document文档下的不同两个元素中也可以使用,该方法可以实现从左边栏列表中选取某些元素加载到右边栏的功能;
document.defaultView属性:
DOM2级视图模块添加了只读的defaultView属性,其中保存着一个指针,指向给定文档所关联的window(或框架),;除IE外所有浏览器都支持该属性;在IE中有一个等价的属性parentWindow,因此,要确定文档的归属窗口,可以如此:
console.log(document.defaultView);
// 除了IE,其它都返回undefined
console.log(document.parentWindow);
console.log(document.defaultView === document.parentWindow);
// 兼容写法
var parentWindow = document.defaultView || document.parentWindow;
DOM2核心还为document.implementation对象规定了两个新方法:
createDocumentType()方法:用于创建一个新的DocumentType节点,接受3个参数:文档类型名称、publicId、systemId;
该方法返回一个DocumentType对象,其可以在创建文档时用在DOMImplementation.createDocument,或者通过Node.insertBefore()或Node.replaceChild()等方法放在文档中;
var doctype = document.implementation.createDocumentType("html",
"-//W3C//DTD HTML 4.01//EN",
"http://www.w3.org/TR/html4/strict.dtd");
console.log(doctype);
console.log(doctype.nodeName); // html
console.log(document.doctype); // null
document.insertBefore(doctype, document.documentElement);
console.log(document.doctype);
如果创建html5文档,如:
var doctype = document.implementation.createDocumentType("html","","");
由于即有文档的文档类型不能改变,因此createDocumentType()只在创建新文档时使用;
var dt = document.implementation.createDocumentType('svg:svg', '-//W3C//DTD SVG 1.1//EN', 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd');
创建的dt这个DocumentType对象,并不是给当前文档使用的,可以把它添加到即将创建的一个新文档;
createDocument()方法:
创建新文档并返回一个Document对象,该方法接受3个参数:针对文档中元素的namespaceURI、文档元素的标签名、新文档的documentType;
var doc = document.implementation.createDocument("","root",null);
会创建一个没有命名空间的新文档,新文档只有文档元素<root>,剩下的所有元素都需要继续添加;
var doctype = document.implementation.createDocumentType("html",
" -//W3C//DTD XHTML 1.0 Strict//EN",
"http://www.w3.org/TR/xhtml1/DTD/xhtml10strict.dtd");
var doc = document.implementation.createDocument("http://www.w3.org/1999/xhtml","html",doctype);
console.log(doc); // #document
会创建一个带有适当命名空间和文档类型的XHTML文档;
再如,创建一个svg文档:
var dt = document.implementation.createDocumentType('svg:svg', '-//W3C//DTD SVG 1.1//EN', 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd');
var d = document.implementation.createDocument('http://www.w3.org/2000/svg', 'svg:svg', dt);
console.log(d.doctype.publicId); // -//W3C//DTD SVG 1.1//EN
创建一个完整的文档:
var doctype = document.implementation.createDocumentType("html","","");
var doc = document.implementation.createDocument("","html",doctype);
console.log(doc); // #document
console.log(doc.documentElement);
var head = document.createElement("head");
doc.documentElement.appendChild(head);
var body = document.createElement("body");
doc.documentElement.appendChild(body);
var h1 = document.createElement("h1");
h1.setAttribute("id","mytitle");
h1.innerText = "零点网络";
h1.style.backgroundColor = "purple";
body.appendChild(h1);
console.log(doc.getElementById("mytitle"));
createHTMLDocument()方法:
DOM2级HTML模块也为document.implementation新增了一个createHTMLDocument()方法,该方法用途是创建一个完整的HTML文档,包括<html>,<head>,<title>和<body>;该方法接受一个参数,即新创建文档的标题,返回新的HTML文档;
var doc = document.implementation.createHTMLDocument("零点网络");
console.log(doc);
console.log(doc.documentElement);
console.log(doc.title);
var h1 = doc.createElement("h1");
h1.innerText = "zeronetwork";
doc.body.appendChild(h1);
通过调用createHTMLDocument()创建的这个文档,是HTMLDocument类型的实例,因而具有该类型的所有属性和方法,包括title和body属性;
示例:创建一个新文档,放插入到当前文档中的iframe中:
<p><button id="btn" onclick="makeDocument()">创建新文档并插入到iframe中</button></p>
<iframe id="theFrame" src="about:blank"></iframe>
<script type="text/javascript">
function makeDocument(){
var frame = document.getElementById("theFrame");
var doc = document.implementation.createHTMLDocument("零点网络");
var p = doc.createElement("p");
p.innerHTML = "Web前端开发课程";
try{
doc.body.appendChild(p);
}catch(e){
console.log(e);
}
// 将新建的HTML文档放到iframe中
var destDocument = frame.contentDocument;
console.log(destDocument);
var srcNode = doc.documentElement;
console.log(doc.documentElement);
var newNode = destDocument.importNode(srcNode, true);
console.log(destDocument); // 并没有变化,执行下句才有变化
destDocument.replaceChild(newNode, destDocument.documentElement);
}
</script>
页面可见性API:
在浏览页面时,任何页面都有可能在后台,对有户不可见;页面可见性API提供了可以观察可见的事件,以便了解文档何时可见或隐藏,以及查看页面当前可见性状态的功能;
页面可见性API对于节省资源和提高性能特别有用,它使页面在文档不可见时避免执行不必要的任务;其提供了两个属性及一个相关的事件;
document.hidden属性:只读属性,返回布尔值,表示页面是true或否false隐藏;
console.log(document.hidden); // false
visibilitychange事件:当document.hidden状态改变时,会触发document对象的这个事件;
console.log(document.hidden); // false
document.addEventListener("visibilitychange",function(e){
console.log("hidden:" + document.hidden);
});
小示例:
<p id="myp"></p>
<script>
var myp = document.getElementById("myp");
var i = 0;
myp.innerHTML = "开始计时:<strong>" + i + "</strong>";
var timeElt = myp.querySelector("strong");
var timer = setInterval(beginTimer, 1000);
function beginTimer(){
if(i==60)
clearInterval(timer);
if(!document.hidden)
timeElt.innerText = ++i;
}
</script>
document.visibilityState属性:只读属性,返回document的可见性(一个VisibilityState对象);通过此属性可以知道当前文档(即为页面)是在背后、或是不可见的隐藏的标签页、或者(正在)预渲染;所以主流浏览器都支持;
可能的值如下:
- 'visible':此时页面内容至少是部分可见,即此页面在前景标签页中,并且窗口没有最小化;
- 'hidden':此时页面对用户不可见,即文档处于背景标签页或者窗口处于最小化状态,或者操作系统正处于'锁屏状态';.
- 'prerender':页面此时正在渲染中, 因此是不可见的;文档只能从此状态开始,永远不能从其他值变为此状态;
// 直接刷新会返回visible;
// 把所有窗口最小化,刷新此页面,会返回hidden;
console.log(document.visibilityState);
当此属性的值改变时,也会触发document对象的visibilitychange事件;
document.addEventListener("visibilitychange",function(){
console.log(document.visibilityState);
});
页面可见性API,最典型的用法是防止当页面正在渲染时加载资源,或者当页面在背景中或窗口最小化时禁止某些活动;其应用的场景如:
- 网站有图片轮播效果,只有在用户观看轮播的时候,才会自动展示下一张幻灯片;
- 显示信息仪表盘的应用程序不希望在页面不可见时轮询服务器进行更新;
- 页面想要检测是否正在渲染,以便可以准确地计算页面浏览量;
- 当设备进行入待机模式时,网站关闭设备声音;
示例:
<video id="myvideo" controls>
<source src="media/shang.mp4" type="video/mp4" />
</video>
<script>
var hidden, visibilityChange;
if(typeof document.hidden !== "undefined"){
hidden = "hidden";
visibilityChange = "visibilitychange";
}else if(typeof document.msHidden !== "undefined"){
hidden = "msHidden";
visibilityChange = "msvisibilitychange";
}else if(typeof document.webkitHidden !== "undefined"){
hidden = "webkitHidden";
visibilityChange = "webkitvisibilitychange";
}
var video = document.getElementById("myvideo");
// 如果是隐藏状态,则暂停视频,否则播放
function handleVisibilityChange(){
if(document[hidden])
video.pause();
else
video.play();
}
if(typeof document.addEventListener === "undefined" ||
typeof document[hidden] === "undefined")
console.log("你的浏览器不支持");
else{
document.addEventListener(visibilityChange, handleVisibilityChange, false);
// 当视频暂停和播放时,设置title
video.addEventListener("pause", function(){
document.title = "已暂停";
},false);
video.addEventListener("play",function(){
document.title = "正在播放";
},false);
}
</script>
注:<iframe>的可见性状态与父文档相同,使用CSS属性隐藏<iframe>不会触发可见性事件或更改框架中包含的文档的状态;
浏览器对不可见页面性能的策略:
除了使用页面可见性API外,浏览器也会采取许多策略来减轻不见可页面对性能的影响,如:
- 大多数浏览器不会调用被隐藏的页面或<iframe>框架中的requestAnimationFrame()定义的回调函数(执行动画时,在下一次重绘之前调用指定的回调函数更新动画),这会提升性能并且延长电池的使用寿命;
- 在不见可页面或iframe框架中setTimeout()等计时器会被加速;
- 在不可见页面或iframe中,限制CPU占有量;
但是某些进行是不受以上的限制的,如:
- 播放音频的页面被视为前台,不受限制;
- 运行使用实时网络连接(WebSockets([?s?k?ts])和WebRTC)的代码的页面将取消锁定,以避免关闭这些连接超时和意外关闭;
- 为了避免超时,IndexedDB进程也不受限制;
4.Node类型的变化:
Node类型中唯一与命名空间无关的变化,就是添加了isSupported()方法;与DOM1级为document.implementation引入的hasFeature()方法类似,isSupported()方法用于确定当前节点具有什么能力;该方法也接受相同的两个参数:特性名和特性版本号;如果浏览器实现了相应特性,而且能够基于给定节点执行该特性,就返回true;
if(document.body.isSupported("HTML", "2.0")){
//执行只有DOM2级HTML才支持的特性
console.log("支持");
}
isSameNode()和isEqualNode()方法:
DOM3级引入了两个比较节点的方法:isSameNode()和isEqualNode();这两个方法都接受一个节点参数,并在传入节点与引用的节点相同或相等时返回true;所谓相同,指的是两个节点引用的是同一个对象;所谓相等,指的是两个节点是相同的类型,具有相等的属性,即attributes属性相同,相同的后代子节点,即childNodes属性也相等;
var div1 = document.createElement("div");
div1.setAttribute("class","box");
var div2 = document.createElement("div");
div2.setAttribute("class","box");
var div3 = div1;
console.log(div1.isSameNode(div2)); // false
console.log(div1.isEqualNode(div2)); // true
console.log(div1.isSameNode(div3)); // true
console.log(div1.isEqualNode(div3)); // true
相同的肯定相等,相等并不一定相同;
对于isEqualNode()方法,其判断的属性是不分顺序的:
<!-- 或者比较两个已有的元素 -->
<div class="mydiv" data-id="1">零点程序员</div>
<div>零点程序员</div>
<div data-id="1" class="mydiv">零点程序员</div>
<script>
var divList = document.getElementsByTagName("div");
console.log(divList[0].isEqualNode(divList[1])); // false
console.log(divList[0].isEqualNode(divList[2])); // true
console.log(divList[0] == divList[2]); // false
</script>
Node.getRootNode(options)方法:返回上下文中的根节点,如果shadow DOM可用,则对shadow DOM同样适用;IE不支持;
var mydiv = document.getElementById("mydiv");
var node = mydiv.getRootNode();
console.log(node); // #document
参数options是可选的,它是一个获取根节点时的可选参数对象;下列值可供选择:
composed:Boolean,如果检索到shadow Root需要返回,则设置为false,默认值,如果跳过shadow Root则设置为true;
返回值:返回一个继承自Node的对象;返回值会因为getRootNode()调用的地方不同而不同; 例如:在HTML网页中调用将会返回一个HTMLDocument对象表示整个网页,在Shadow DOM里调用将会返回一个与之相关联的ShadowRoot;
console.log(node.getRootNode({composed: true})); //#document
compareDocumentPosition(otherNode)方法:其是在DOM Level 3级中定义的,其比较当前节点与任意文档中的另一个节点的位置关系,也是用来确定节点间的关系;其返回一个表示该关系的位掩码(bitmask);
- 常量名 十进制值 含义
- DOCUMENT_POSITION_DISCONNECTED 1 无关,不在同一文档中
- DOCUMENT_POSITION_PRECEDING 2 居前,otherNode在node之前
- DOCUMENT_POSITION_FOLLOWING 4 居后,otherNode在node之后
- DOCUMENT_POSITION_CONTAINS 8 包含,otherNode包含node
- DOCUMENT_POSITION_CONTAINED_BY 16 被包含,otherNode被node包含
- DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC 32 待定
var mydiv = document.getElementById("mydiv");
console.log(document.body.contains(mydiv)); // true
var result = document.documentElement.compareDocumentPosition(document.body);
console.log(result); // 20
console.log(result & 16); // 16
console.log(!!(result & 16)); // true
返回20,表示居后的4加上表示被包含的16;
因为compareDocumentPosition返回的是一个位掩码,所以必须再使用按位与运算符才能得到有意义的值;
var head = document.getElementsByTagName('head').item(0);
if (head.compareDocumentPosition(document.body) & Node.DOCUMENT_POSITION_FOLLOWING) {
console.log("前");
} else {
console.log("后");
}
Node.baseURI属性:是只读属性,返回一个节点的base URL,如果无法获取则可能返回 null;
var node = document.getElementsByTagName("div")[0];
console.log(node.baseURI); // http://127.0.0.1:5500/temp.html
一般情况下,base URL就是document的location,但是它受诸多方面因素的影响,例如HTML的<base> 元素和 XML xml:base 属性;
<!-- 添加base后结果就不一样了 -->
<base href="https://www.zeronetwork.cn/">
<script>
var node = document.getElementsByTagName("div")[0];
console.log(node.baseURI); // https://www.zeronetwork.cn/
console.log(location.href); // http://127.0.0.1:5500/temp.html
console.log(document.URL); // http://127.0.0.1:5500/temp.html
console.log(document.documentURI); // http://127.0.0.1:5500/temp.html
</script>
其实这个属性HTML文档中没有什么用途,另外IE也不支持该属性;
Node.isConnected属性:是一个只读属性,返回一个布尔值用来检测该节点是否已连接(直接或者间接)到一个上下文对象上,比如:Document对象与一般DOM树连接,ShadowRoot与shadow DOM连接;IE不支持;
var node = document.getElementsByTagName("div")[0];
console.log(node.isConnected); // true
var div = document.createElement("div");
console.log(div.isConnected); // false;
document.body.appendChild(div);
console.log(div.isConnected); // true;
猜你喜欢
- 2024-11-27 video.js中文文档(三):设置
- 2024-11-27 高级前端进阶:我是如何把 C/C++ 代码跑在浏览器上的?
- 2024-11-27 原生微信小程序video隐藏控件超级方法
- 2024-11-27 浏览器播放rtsp视频流解决方案
- 2024-11-27 使用AI CoNR 算法,仅仅利用4张动漫图片——便可以创建舞蹈视频
- 2024-11-27 WebRTC是如何实现音视频的录制
- 2024-11-27 WebAssembly该怎么学第二篇
- 2024-11-27 WebRTC录采集平面数据
- 2024-11-27 JAVA全栈CMS系统vue图片/视频上传组件,多图上传及删除功能11
- 2024-11-27 简单易懂的大文件上传教程:Vue3与.NET 6完美结合
- 最近发表
- 标签列表
-
- xml (46)
- css animation (57)
- array_slice (60)
- htmlspecialchars (54)
- position: absolute (54)
- datediff函数 (47)
- array_pop (49)
- jsmap (52)
- toggleclass (43)
- console.time (63)
- .sql (41)
- ahref (40)
- js json.parse (59)
- html复选框 (60)
- css 透明 (44)
- css 颜色 (47)
- php replace (41)
- css nth-child (48)
- min-height (40)
- xml schema (44)
- css 最后一个元素 (46)
- location.origin (44)
- table border (49)
- html tr (40)
- video controls (49)