<html>
<head>
<title>专有扩展</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript">
window.onload = function(){
/*
1、文档模式
IE8引人了一个新概念叫“文档模式”(document mode)。页面的文档模式决定可以使用什么
功能。换句话说,文档模式决定了你可以使用哪个级别的CSS,可以在JavaScript中使用哪些
API,以及如何对待文档类型(doctype)。到了IE9,总共有以下四种文档模式。
IE5:以混杂模式渲染页面(IE5的默认模式就是混杂模式)。IE8及更高的版本中无法使用。
IE7:以IE7标准模式渲染页面。IE8及更高的版本无法使用。
IE8:以IE8标准模式渲染页面。IE8中的新功能都可以使用,因此可以使用Selectors API、
更多的CSS2级选择符和某些CSS3功能,还有一些HTML5功能。不过IE9中的新功能无法
使用。
IE9:以IE9标准模式渲染页面。IE9中的新功能都可以使用,比如ECMAScript5、完整的CSS3
以及更多的HTML5功能。这个文档模式是最高级别的模式。
要理解IE8及更高版本的工作原理,必须理解文档模式。
要强制浏览器以某种文档模式渲染页面,可以使用HTTP头部信息X-UA-Compatible,或者通过
等价的<meta>标签设置:
<meta http-equiv="X-UA-Compatible" content="IE-IEVersion">
注意,这里的IE的版本(IEVersion)有一下一些不同的值,而这些值并不一定与上面的四种
文档模式对应。
Edge:始终使用最新的文档模式来渲染页面。忽略文档类型的声明。对于IE8,始终保持以IE8
标准模式渲染页面。对于IE9,则以IE9标准模式渲染页面。
EmulateIE9:如果有文档类型声明,则以IE9的标准模式渲染页面,否则将文档模式设置为IE5。
EmulateIE8:如果有文档类型声明,则以IE8标准模式渲染页面,否则将文档模式设置为IE5。
EmulateIE7:如果有文档类型声明,则以IE7标准模式渲染页面,否则将文档模式设置为IE5。
9:强制以IE9标准模式渲染页面,忽略文档声明。
8:强制以IE8准模式渲染页面,忽略文档声明。
7:强制以IE7标准模式渲染页面,忽略文档声明。
5:强制以IE5标准模式渲染页面,忽略文档声明。
比如,想让想让文档模式像IE7中一样,可以使用下面代码:
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7">
如果不打算考虑文档类型声明,而直接使用IE7标准模式,那么可以使用下面这行代码:
<meta http-equiv="X-UA-Compatible" content="IE7">
如果不打算考虑文档类型声明,而直接使用IE7标准模式,那么就可以使用下面这行代码:
<meta http-equiv="X-UA-Compatible" content="IE7">
如果不打算考虑文档类型声明,而直接使用IE7标准模式,那么可以使用下面这行代码:
<meta http-equiv="X-UA-Compatible" content="IE7">
没有规定必须在页面中设置X-UA-Compatible。默认情况下,浏览器会通过文档类型声明来确定
是使用最佳的可用文档模式,还是使用混杂模式。
通过document.documentMode 属性可以知道给定页面使用的什么文档模式。这个属性是IE8
中新增的,它会返回使用的文档模式版本号(在IE9中,可能返回版本号为5、7、8、9);
var mode = document.documentMode;
知道页面采用的是什么文档模式,有助于理解页面的行为方式。无论在什么文档模式下,都
可以访问这个属性。
2、children属性
由于IE9之前的版本与其他的浏览器在处理文本节点中的空白字符时有差异,因此就出现了children
属性。这个属性是HTMLCllection的实例,只包含元素中同样还是元素的子节点。除此之外,children
属性与childNodes没有什么区别,即在元素只包含子节点是,这两个属性的值相同。下面访问children
属性的的实例代码:
var childCount = element.children.length;
var firstChild = element.children[0];
支持children属性的浏览器有IE5、Firefox3.5、Safari2(有bug)、Safari3(完全支持)、Opera8和Chrome
(所有版本)。IE8及更早的版本的children属性中也会包含注释节点,但IE9之后的版本则只返回元素
节点。
3、contains()方法
在实际开发中,经常需要知道某个节点是不是另一个节点的后代。IE为此率先引入了contains()
方法,以便不通过DOM文档树中查找即可获得这个信息。调试用contains()方法应该是祖先节点
也就是搜索开始的节点,这个方法接收一个参数,即要检测的后代节点。如果被检测的节点是后代
节点,该方法返回true;否则,返回false。以下是一个例子:
alert(document.docuemntElement.contains(document.body));//true
这个例子测试<body>元素是不是<html>元素的后代节点,在格式正确的HTML页面中,以上代码
返回true。支持contains()方法的浏览器有IE、FireFox 9+、Safari、Opera和Chrome。
使用DOM Level 3 compareDocmentPosition()也能确定节点之间的关系。支持这个方法的浏览器
有IE9+、Firefox、Safari、Oprea 9.5+和Chrome。如前所述,这个方法用于确定两个节点之间的关系
,返回一个表示该关系的掩码的值。
掩码 节点关系
1 无关(给定的节点不在当前文档中)
2 居前(给定的节点在DOM树中位于参考节点之前)
4 居后(给定的节点在DOM树中位于参考节点之后)
8 包含(给定的节点是参考节点的祖先)
16 被包含(给定的节点是参考节点的后代)
为了模仿contains()方法,应该关注的是掩码16。可以对compareDocumentPosition()的结果
执行安位与,以确定参考节点(调用compareDocumentPosition()方法的当前节点)是否包含
给定的节点(传入的节点)。来看下面的例子:
*/
var result = document.documentElement.compareDocumentPosition(document.body);
alert(!!(result&16));
/*
执行上面代码后,结果会变成20(表示“居后”的4加上表示“被包含”的16)。对掩码执行
按位操作符返回以非零的数字,两个逻辑非操作符会将该数值转换为布尔值。
使用一些浏览器及能力检测,就可以写出如下的通用的contains函数
*/
/*
这个函数是一个组合使用了三种方式确定一个节点是不是另一个节点的后代。函数的第一个
参数就是节点,第二个参数是要检测节点。在函数体内,首先检测refNode是否存在contains()
方法(能力检测)。这一部分代码还检测了当前浏览器使用webkit的版本号。如果方法存在而
不是webkit(!client.engine.webkit)。则继续执行代码。否则,如果浏览器是WebKit至少是
Safari3(webkit版本号为522或者更高),那么也可以继续执行代码。在WebKit小于522的Safari
浏览器中,contains()方法不能正常使用。
接下来检测是否存在compareDocumentPosition()方法,而函数最后一个则是自otherNode开始
向上遍历DOM结构,以递归方式取得parentNode,并检查是否与refNode相等。在文档树顶端,
parentNode的值等于null,于是循环结束。这是针对旧版的Safari设计的后备策略。
*/
function contains(refNode,otherNode){
if(typeof refNode.contains == "function" &&
!(client.engine.webkit || client.engine.webkit >= 522)){
return refNode.contains(otherNode);
}else if(typeof refNode.compareDocumentPosition == "function"){
return !!(refNode.compareDocumentPosition(otherNode) && 16);
}else{
var node = otherNode.parentNode;
do{
if(node == refNode){
return true;
}else{
node = node.parentParent;
}
}while(node != null);
return false;
}
}
//测试contains(refNode,otherNode);
alert(contains(document.documentElement,document.body));
/*
4、插入文本
前面介绍过,IE原来专有的插入标记的属性innerHTML和outerHTML以及被纳入了规范。
但是另外的两个插入文本的专有属性没有这样的好多运气。这两是没有被HTML5看中的属性是
InnerText和outerText
a.innerHTML属性
通过innerText属性可以操作元素中包含的所有的文本内容,包括子文档树中的文本。在通过
innerText读取值时,他会按照由浅入深的顺序,将子文档树中的所有文本拼接起来。在通过
innerText写入值,结果会删除元素的所有子节点,插入包含相应文本值的文本节点。来看下面
这个HTML代码实例。
<div id="content">
<p>This is a <strong>paragraph</strong>with a list following it.</p>
<ul>
<li>Item 1</li>
<li>Item 2</li>
<li>Item 3</li>
</ul>
</div>
对于这个例子中的<div>元素而言,起innerText属性会返回下列字符串
This is a paragraph with a list following it.
Item 1
Item 2
Item 3
由于不同的浏览器处理空白符的方式不同,因此输入的文本可能会也不包含原始的HTML代码的
收进。
使用innerText属性设置这个<div>元素的内容,则只需要一行代码:
div.innerText = "Hello world !";
执行这行代码后,页面的HTML代码会变成如下所示。
<div id="content">Hello world!</div>
设置innerText属性移除了先前存在的所有子节点,完全改变了DOM子树。此外,设置innerText
属性的同时,也对文本中存在的HTML语法字符(小于号、大于号、引号和号)进行编码。在看看
一个例子。
div.innerText = "Hello & welcom, <b>\"reader\"!</b>";
运行以上代码会得到如下结果
<div id="content">Hello &welcome,<b>"reader"!</b></div>
设置innerText永远只会生成当前节点的一个子文本节点,而为了确保只生成一个子文本
节点,就必须要对文本进行HTML编码。利用这一点,可以通过innerText属性过滤掉HTML标签。
方法是将innerTxt设置等于innerText,这样就可以去掉所有的HTML标签,比如:
div.innerText = div.innerText;
执行这行代码之后,就用原来的文本内容替换了容器元素中的所有内容(包括子节点,因而也就
去掉了HTML标签)。
支持innerText属性的浏览器包括IE4+、Safari3+、Opera8+和Chrome。Firfox虽然不支持
innerText,但支持作用类似的textContent属性。textContent是DOMLevel3规定的一个属性,其
他支持textContent属性的浏览器还有IE9+、Safari3+、Opera10+和Chrome。为了确保浏览器兼容
有必要编写一个类似于下面的函数来检测可以使用哪个属性。
*/
function getInnerText(element){
return (typeof element.textContent == "string")?
element.textContent:element.innerText;
}
function setInnerText(element,text){
if(typeof element.textContent == "string"){
element.textContent = text;
}else{
element.innerText = text;
}
}
/*
这两个函数都接收一个元素作为参数,然后检查这个元素是不是有textContent属性。
如果有,那么typeof element.textContent 应该是"string";如果没有,那么这两个
函数就会改为使用innerText。可以像下面这样调用这两个函数。
*/
var div = document.querySelector("#test1");
setInnerText(div,"Hello world!");
alert(getInnerText(div));//Hello world!
//使用这两个函数可以确保在不同浏览器中使用正确的属性。
/*
实际上,innerText与textContext返回的内容并不完全一样。比如,innerText会忽略
行内的样式和脚本代码。避免跨浏览器兼容性问题的最佳途径,就是从不包含行内的样式
或行内脚本的DOM子树副本或者DOM片段中读取文本。
b.outerText属性
除了作用范围扩大到了包含调用它的节点之外,outerText与innerText基本上没有多大区别。在
读取文本值时,outerText与innerText的结果是完全一样。但在写模式下,outerText就会完全
不同了:outerText不只是替换调用它的元素的子节点,而是会替换整个元素(包括子节点)。
比如:
*/
div.outerText = "Hello world!";
/*这行代码实际上相当于如下两行代码:
var text = document.createTextNode("Hello world!");
div.parentNode.replaceChild(text,div);
本质上,新的文本节点会完全取代调用outerText的元素。此后该元素就从该文档中被删除,
无法访问。
支持outerText属性的浏览器有IE4+、Safari3+、Oprea8+和Chrome。由于这个属性会
导致调用的元素不存在,因此并不常用。我们也介意读者尽可能不要使用这个属性。
4、滚动
如前所叙,HTML5之前的规范并没有就与页面滚动相关的API做的任何规定。但HTML5在将
scrollIntoView()纳入了规范之后,仍然还有几个专有的方法可以在不同的浏览器中使用。
下面列出几个方法都是HTMLElement类型的扩展,因此在所有元素中都可以调用。
a.scrollIntoViewIfNeeded(alignCenter):只在当前元素在视口中不可见的情况下,才
滚动浏览器窗口或者容器元素,最终让他可见。如果当前元素中的视口中可见,这个方法什么
也不做。如果可以将可选的alignCenter设置为true,则表示尽量将元素在视口中部(垂直方向)。
Safari和Chrome实现了这个方法。
b、scrollByLines(lineCount):将元素的内容滚动指定的行高,lineCount值可以是正值也可以是
负值。Safari和Chrome实现了这个方法。
c、scrollByPages(pageCount):将元素的内容滚动指定的页面高度,具体高度由元素的高度决定。
Safari和Chrome实现了这个方法。
希望大家注意的是,scrollIntoView()和scrollIntoViewIfNeeded()的作用对象是元素的容器,而
scrollByLines()和scrollByPages()影响则是元素自身。下面还有几个事例吧。
*/
//将页面主体滚动五行
document.body.scrollBylines(5);
//在当前元素不可见的时候,让他进入浏览器的视口
document.getElementById("test1").scrollIntoViewIfNeeded();
//将页面主动向回滚动1页
document.body.scrollByPages(-1);
//由于scrollIntoView是唯一一个所有浏览器支持的方法、因此还是该方法最常用。
}
</script>
</head>
<body>
<div id="test1"></div>
</body>
<script type="text/javascript">
/*
浏览器的相关检测函数
*/
var client=function(){
var engine={ //呈现引擎
trident:0,
gecko:0,
webkit:0,
khtml:0,
presto:0,
ver:null //具体的版本号
};
var browser={ //浏览器
ie:0,
firefox:0,
safari:0,
konq:0,
opera:0,
chrome:0,
ver:null //具体的版本号
};
var system={ //操作系统
win:false,
mac:false,
x11:false
};
var ua=navigator.userAgent;
if(/AppleWebKit\/(\S+)/.test(ua)){ //匹配Webkit内核浏览器(Chrome、Safari、新Opera)
engine.ver=RegExp["$1"];
engine.webkit=parseFloat(engine.ver);
if(/OPR\/(\S+)/.test(ua)){ //确定是不是引用了Webkit内核的Opera
browser.ver=RegExp["$1"];
browser.opera=parseFloat(browser.ver);
}else if(/Chrome\/(\S+)/.test(ua)){ //确定是不是Chrome
browser.ver=RegExp["$1"];
browser.chrome=parseFloat(browser.ver);
}else if(/Version\/(\S+)/.test(ua)){ //确定是不是高版本(3+)的Safari
browser.ver=RegExp["$1"];
browser.safari=parseFloat(browser.ver);
}else{ //近似地确定低版本Safafi版本号
var SafariVersion=1;
if(engine.webkit<100){
SafariVersion=1;
}else if(engine.webkit<312){
SafariVersion=1.2;
}else if(engine.webkit<412){
SafariVersion=1.3;
}else{
SafariVersion=2;
}
browser.safari=browser.ver=SafariVersion;
}
}else if(window.opera){ //只匹配拥有Presto内核的老版本Opera 5+(12.15-)
engine.ver=browser.ver=window.opera.version();
engine.presto=browser.opera=parseFloat(engine.ver);
}else if(/Opera[\/\s](\S+)/.test(ua)){ //匹配不支持window.opera的Opera 5-或伪装的Opera
engine.ver=browser.ver=RegExp["$1"];
engine.presto=browser.opera=parseFloat(engine.ver);
}else if(/KHTML\/(\S+)/.test(ua)||/Konqueror\/([^;]+)/.test(ua)){
engine.ver=browser.ver=RegExp["$1"];
engine.khtml=browser.konq=parseFloat(engine.ver);
}else if(/rv:([^\)]+)\) Gecko\/\d{8}/.test(ua)){ //判断是不是基于Gecko内核
engine.ver=RegExp["$1"];
engine.gecko=parseFloat(engine.ver);
if(/Firefox\/(\S+)/.test(ua)){ //确定是不是Firefox
browser.ver=RegExp["$1"];
browser.firefox=parseFloat(browser.ver);
}
}else if(/Trident\/([\d\.]+)/.test(ua)){ //确定是否是Trident内核的浏览器(IE8+)
engine.ver=RegExp["$1"];
engine.trident=parseFloat(engine.ver);
if(/rv\:([\d\.]+)/.test(ua)||/MSIE ([^;]+)/.test(ua)){ //匹配IE8-11+
browser.ver=RegExp["$1"];
browser.ie=parseFloat(browser.ver);
}
}else if(/MSIE ([^;]+)/.test(ua)){ //匹配IE6、IE7
browser.ver=RegExp["$1"];
browser.ie=parseFloat(browser.ver);
engine.ver=browser.ie-4.0; //模拟IE6、IE7中的Trident值
engine.trident=parseFloat(engine.ver);
}
var p=navigator.platform; //判断操作系统
system.win=p.indexOf("Win")==0;
system.mac=p.indexOf("Mac")==0;
system.x11=(p.indexOf("X11")==0)||(p.indexOf("Linux")==0);
if(system.win){
if(/Win(?:dows )?([^do]{2})\s?(\d+\.\d+)?/.test(ua)){
if(RegExp["$1"]=="NT"){
switch(RegExp["$2"]){
case "5.0":
system.win="2000";
break;
case "5.1":
system.win="XP";
break;
case "6.0":
system.win="Vista";
break;
case "6.1":
system.win="7";
break;
case "6.2":
system.win="8";
break;
case "6.3":
system.win="8.1";
break;
case "6.4":
system.win="10";
break;
case "10":
system.win="10";
break;
default:
system.win="NT";
break;
}
}else if(RegExp["$1"]=="9x"){
system.win="ME";
}else{
system.win=RegExp["$1"];
}
}
}
return {
ua:ua, //用户浏览器Ua原文
engine:engine, //包含着用户浏览器引擎(内核)信息
browser:browser,//包括用户浏览器品牌与版本信息
system:system //用户所用操作系统及版本信息
};
}();
</script>
</html>
DOM_专有扩展
最新推荐文章于 2022-07-29 09:00:17 发布