Fork me on GitHub
前端面霸系列(1):doctype 、Quirks Mode & Standards Mode 、document.compatMode

前端面霸系列(1):doctype 、Quirks Mode & Standards Mode 、document.compatMode

近几日,气压猛降,雾霾铺天盖地,眼看一场腥风血雨就要前端江湖爆发,这场战争不仅是百度、腾讯、阿狸、搜狐网易新浪等江湖豪门抢夺人才的大战,也是诸位江湖人士重新洗牌的好时机。每年10月,江湖的波动胜过华山论剑、五岳争主。如今这江湖人才济济,新门派如雨后春笋,江湖高手更是各个身怀绝技。在博客园这个擂台上更是齐聚各路高手。本人wisdomoon是一名懒人,没有"汤姆大叔"的盖世神功,也没有"T2噬菌体"的严谨细腻,更没有"Aaron"的精益求精,因此本人为大家献上的不是怒秀技巧的《葵花宝典》、不是移花接木的《辟邪剑谱》、也不是磨砺内力的《易筋经》而是"投机取巧"的《前端面霸系列》。阅完此系列,诸位会略知各门派开山祖师的入门绝技,如果运用自如,能顺利潜入任何门派,到时学一家之长不在话下。

  由于是第一篇,以上所述就当引言。本秘籍将采用Q&A(Question and Answer)的方式带大家一览各派问题。

  Q:doctype的作用是什么,你知道多少种类型的doctype

  A: 最简单也最常见的回答是:doctype是DocumentType的简称即文档类型,常见的有 Strict、Transitional 以及 Frameset 三种。一般一个问题后面会有"为什么"、"会导致什么",这才是这道题目的关键。文档为什么要分类型?如果文档不分类型,各浏览器就会形成多种写法,假如IE用<title>标签作为标题、火狐浏览器用<caption>标签作为标题,而另一种浏览器可能采用<mytitle>,这对于开发者和用户来说简直是灾难。这时候神僧W3C(万维网联盟World Wide Web Consortium)已经把自己偷偷熬夜制作的对所有方面都平衡的分歧解决方案拿了出来,各浏览一看内心计算片刻之后纷纷表示没有异议。于是我们用<!doctype>(注意:作为一个特殊的标签,它是不需要闭合的)标签来引入W3C的dtd文件,以达到规范页面的效果。此举壮大了w3c组织,同时、浏览器的文档标准的统一也为开发人员和用户带了了便利。 这里需要提出的是html5不是基于SGML实现的,因此html不需要引入DTD,所以html5可以简单的声明一下<!doctype html>。

  Q:如果不声明doctype?

  A:这也就是上面所述的"会导致什么"。如果不声明doctype,浏览器不引入w3c的标准,那么早期的浏览器会按照自己的解析方式渲染页面。我们把不使用w3c标准解析,采用自身方式解析页面的行为称为"quirks mode",相反的,采用w3c方式解析就是"strict mode"也就是我们说的"standards mode"。我们知道凡是不可能太完美,如果完全采用strictmode那么不允许出任何的差错,这样会降低开发人员和用户的好感,因此在standards mode 里面我们又分出来 Almost Standards Mode(接近标准模式),如下图:

   <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN "http://www.w3.org/TR/html4/loose.dtd">在html4中我们引入最多的就是接近标准的Transitional 文档类型。这两个问题考察了我们是否深入理解doctype的类型,是否理解浏览器的解析和渲染规则。还请多多揣摩。

  这个问题的最直接答案是:不写doctype,浏览器会进入quirks mode (混杂模式)

  Q:如何判断浏览使用哪种方式解析css

  A:这其实是上面问题的复习和延伸。直接给出答案:

    1、没有doctype声明的采用quirks mode解析

    2、对于有doctype的大多数采用standard mord(这里为什么用大多数,请读者思考,请参考http://hsivonen.iki.fi/doctype/

    3、特殊情况:

      a、对于那些浏览器不能识别的doctype ,浏览器采用quirks mode

      b、没有声明DTD或者html版本声明低于4.0采用quirks mode其他使用standard mode

      c、你能看到的现在的大多数网页采用的是standard mode

      d、ie6中,如果在doctype声明前有一个xml声明(比如:<?xml version="1.0" encoding="iso-8859-1"?>),则采用quirks mode解析。(这点估计连面试官都没注意)

  Q:说出quirks mode 和 strict mode 在浏览器上的表现差异?

  A:他们最大的不同是对盒模型(由于本人对盒模型理解模糊,再加上盒模型是笔试、面试常见题目,后面会专门来一期一期探讨)的解析。

    在strict mode中 :

      width是内容宽度 ,也就是说,元素真正的宽度 = margin-left + border-left-width + padding-left + width + padding-right + border-right-width + margin-right;

    在quirks mode中 :

      width则是元素的实际宽度 ,内容宽度 = width - (margin-left + margin-right + padding-left + padding-right + border-left-width + border-right-width)

  Q:js如何判断浏览器正在以何种方式解析?

  A:这道问题的答案很简答,但是重要的往往不是答案。重要的是要知道为什么?(是不是被老套路搞烦了,那么听听歌,给看官推荐一首 《相思风雨中 张学友》)。"到底是为什们呢?"这是我和朋友最喜欢开玩笑的口语。好了,回归正传,这里打算用一个例子说明。

jiathis的源代码中有如下细节:

    

复制代码
     var d = document,
         isStrict = d.compatMode == "CSS1Compat",
         dd = d.documentElement,
         db = d.body
         /*省略一部分无关代码*/
      return {
            h: (isStrict ? dd : db).clientHeight,
            w: (isStrict ? dd : db).clientWidth
       }    
复制代码

看到这里本题的答案也出来了使用 document.compatMode来判断浏览器的解析方式,代码如下

复制代码
function getMode{
    var _cm = docoment.compatMode;
    if(_cm == 'CSS1Compat'){
         return "strict"
    } 
    if(_cm == 'BackCompat' ){
         return  'quirks'
    } 
}
复制代码

  我们经常使用document.body是因为我们大多数处理的网页都是strict的,如果页面的开发者忘了写doctype我们就必须使用document.documentElement了,这在很多开源框架的源码里很常见。

  Q:何时使用document.body 何时使用document.documentElement

  A:这是免费送大礼,看了上面的同志都轻而易举。

 

 

jQuery的attr与prop

 

jQuery1.6中新添加了一个prop方法,看起来和用起来都和attr方法一样,这两个方法有什么区别呢?这要从HTMl 的attribute与property区别说起,attr与prop正是这两个东西的缩写。

attribute与property

attribute和property都可以翻译为属性,为了以示区别,通常把这两个单词翻译为属性与特性。

<div id="test">Click Hear</div>

上面这段HTML语句中有三个节点,分别是Element “div”、attribute “id”、Text “click hear”,我们最常见的attribute正式指的attribute类型节点,在JavaScript有专门处理attribute的函数 .etAttribute(name) / setAttibute(name,value)。当然attribute不只是我们能够在HTML文档上看到的这几个,我们可以自定义attributed加到DOM节点中

复制代码
<div id="test">123</div>
    
    <script type="text/javascript">
        var t=document.getElementById('test');
        t.setAttribute('class','active');
        t.setAttribute('customizedAttr','customized');
    </script>
复制代码

这样可以div被修改为

<div id="test" class="active" customizedattr="customized">123</div>

通过方法 setAttribute设置的attribute最终都会反映到元素的attribute类型的节点中

property是DOM对象的字段,跟我们平常使用的一些对象一样,包含很多字段,这些字段就是property,取值或者设置值和普通字段一样通过”对象.字段“的方式。

看起来attribute和property应该没有什么关系才对,怎么会。。。attribute和property容易混倄是因为很多attribute节点还有一个相对应的property属性,比如上面div的”id“ attribute 同样可以用t.id取到(实际上绝大部分人都是这样获取的),通过property更改id后,用getAttibute获取的id是更新后的id。

t.id='test1';
console.log(t.getAttribute('id'));//test1

同样我们也可以自定义property

t.customizedProp='customized prop';

区别

1. 于build-in属性,attribute和property共享数据,attribute更改了会对property造成影响,反之亦然,但是两者的自定义属性是独立的数据,即使name一样,也互不影响,看起来是下面这张图,但是IE6、7没有作区分,依然共享自定义属性数据

2. 并不是所有的attribute与对应的property名字都一致,比如刚才使用的attribute 的class属性,使用property操作的时候应该是这样className

t.className='active2';

3. 对于值是true/false的property,类似于input的checked attribute等,attribute取得值是HTML文档字面量值,property是取得计算结果,property改变并不影响attribute字面量,但attribute改变会一向property计算

<input id="test3" type="checkbox"/>
复制代码
var t=document.getElementById('test3');
        console.log(t.getAttribute('checked'));//null
        console.log(t.checked);//false;
        
        t.setAttribute('checked','checked');
        console.log(t.getAttribute('checked'));//checked
        console.log(t.checked);//true
        
        t.checked=false;
        console.log(t.getAttribute('checked'));//checked
        console.log(t.checked);//false
复制代码

 

4. 对于一些和路径相关的属性,两者取得值也不尽相同,但是同样attribute取得是字面量,property取得是计算后的完整路径

<a id="test4" href="#">Click</a>
var t=document.getElementById('test4');
        console.log(t.getAttribute('href'));//#
        console.log(t.href);//file:///C:/Users/bsun/Desktop/ss/anonymous.html#

关于浏览器(IE)造成的兼容性问题可以看看IE 混淆了 DOM 对象属性(property)及 HTML 标签属性(attribute),造成了对 setAttribute、getAttribute 的不正确实现

attr和prop

相信看完上面内容,大家就明白为什么jQuery要添加prop方法了,在jQuery API中也有专门解释

Attributes VS. Properties

在一些特殊的情况下,attributes和properties的区别非常大。在jQuery1.6之前,.attr()方法在获取一些attributes的时候使用了property值,这样会导致一些不一致的行为。在jQuery1.6中,.prop()方法提供了一中明确的获取property值得方式,这样.attr()方法仅返回attributes。

比如,selectedIndextagNamenodeNamenodeTypeownerDocumentdefaultChecked, 和defaultSelected应该使用.prop()方法获取/设置值。 在jQuery1.6之前这些不属于attribute的property需要用.attr()方法获取。这几个并没有相应的attibute,只有property。

关于布尔类型 attributes,比如一个这样的HTML标签,它在JavaScript中变量名为elem

<input type="checkbox" checked="checked" />
elem.checked true (Boolean) Will change with checkbox state
$( elem ).prop( "checked" ) true (Boolean) Will change with checkbox state
elem.getAttribute( "checked" ) "checked" (String) Initial state of the checkbox; does not change
$( elem ).attr( "checked" ) (1.6) "checked" (String) Initial state of the checkbox; does not change
$( elem ).attr( "checked" ) (1.6.1+) "checked" (String) Will change with checkbox state
$( elem ).attr( "checked" ) (pre-1.6) true (Boolean) Changed with checkbox state

根据W3C forms specification,checked属性是一个布尔值,这就意味着只要checked属性在HTML中表现出来了,那么相应的property就应该是true,即使checked没有值,这点儿对其它布尔类型的属性一样适用。

然而关于checked 属性需要记住的最重要的一点是:它和checked property并不是一致的。实际上这个attribute和defaultChecked property一致,而且只应该用来设置checkbox的初始值。checked attribute并不随着checkedbox的状态而改变,但是checked property却跟着变。因此浏览器兼容的判断checkebox是否被选中应该使用property

if ( elem.checked )
if ( $( elem ).prop( "checked" ) )
if ( $( elem ).is( ":checked" ) )

这对其它一些类似于selected、value这样的动态attribute也适用。

在IE9之前版本中,如果property没有在DOM元素被移除之前删除,使用.prop()方法设置DOM元素property(简单类型除外:number、string、boolean)的值会导致内存泄露。为了安全的设置DOM对象的值,避免内存泄露,可以使用.data()方法。

使用场景

其实明白了上面讲的内容,什么时候该使用.attr()什么时候该使用 .prop()就很清楚了,不过还是传一张坊间很流行的图

image

 
 
分类: JavaScript
 
分类: 面霸系列
posted on 2013-10-01 19:28  HackerVirus  阅读(264)  评论(0编辑  收藏  举报