zoukankan      html  css  js  c++  java
  • CSS魔法堂:"那不是bug,是你不懂我!" by inline-block

    前言

     每当来个需要既要水平排版又要设置固定高宽时,我就会想起display:inline-block,还有为了支持IE5.5/6/7的hack*display:inline;*zoom:1;。然后发现盒子间无端端多了个不可选的空白符,于是想尽办法修复这个bug。
     直到一天拜读了@一丝姐、@HAX等高人的秘笈后才顿悟,原来我错了。那不是bug,是我不懂而已。

    先行者——IE5.5中的inline-block

     当我们为支持IE5.5/6/7而添加这段hack时*display:inline;*zoom:1,总以为从IE8开始才支持display:inline-block属性值。其实从IE5.5开始已经支持了,只是IE5.5/6/7支持的是IE的自定义标准,而从IE8开始则是支持CSS2.1标准而已。
    https://msdn.microsoft.com/library/ms530751%28v=vs.85%29.aspx

    The inline-block value is supported starting with Internet Explorer 5.5. You can use this value to give an object a layout without specifying the object’s height or width.

    <style type="text/css">
    .bk1{
      background: #06F;
    }
    .bk2{
      background: #F60;
    }
    .item{
       100px;
      height: 100px;
      display:inline-block;
    }
    </style>
    <div class="bk1 item"></div>
    <div class="bk2 item"></div>
    <span class="bk1 item"></span>
    <span class="bk2 item"></span>
    


     经过CSS2.1洗礼的我们对上述内容不禁会发出两个疑问:

    1. 为啥block-level element设置了display:inline-block后还是垂直方向排列呢?
    2. 为啥inline-level element设置了display:inline-block后之间没有诡异的间隙呢?

     还记得杨过是如何变成神雕大侠的吗?不就是被断右臂后发现左手才是真爱吗:)
    好了,其实我的意思是抛弃过去对display:inline-block的认知,重新来理解IE5.5/6/7下的它才是硬道理啦。
     对于问题1,首先上面的引用很直白地告诉我们——display:inline-block能触发hasLayout,然后就没了。所以block-level element依然是block-level element,不会一夜成了inline-level element的。结论:display:inline-block仅会触发hasLayout,而元素本该怎么排版还是怎么排版。关于hasLayout的内容可参考《CSS魔法堂:hasLayout原来是这样!》
     对于问题2,我们先看看是否真的没有间隙吧!

    <style type="text/css">
    .bk1{
      background: #06F;
    }
    .bk2{
      background: #F60;
    }
    .item{
       100px;
      height: 100px;
      display:inline-block;
    }
    </style>
    <span class="bk1 item"></span>
    <span class="bk2 item"></span>
    <br/><br/>
    <span class="bk1 item">bk1</span>
    <span class="bk2 item"></span>
    


     见鬼了,在前一个盒子内添加些文本就出现间隙了?其实这真的和display:inline-block无关的,大家就放过他吧!来上呈堂证供!

    <style type="text/css">
    .bk1{
      background: #06F;
    }
    .bk2{
      background: #F60;
    }
    <span class="bk1">no line break</span>
    &#x20;&#x20;
    &#x20;&#x20;
    <span class="bk2">
    has line break
    </span>
    


     可以看到蓝色块k和红色块h间存在一个空格,而红色块k后也存在一个空格。可是代码中我们看到蓝红色块间有4个&#x20HTML实体,为啥只有一个空格呢?而红色块中仅仅换了行而已,怎么就有个空格呢?
     先抛结论:上面两端代码均是white space、white space collasping再作祟。

    White space不仅是空格符那么简单

     初看之下以为就是空格键,其实white space是一组空白字符和换行符组成。查看unicode字符集我们会发现有一大堆空白字符(如NO-BREAK SPACE等),但HTML只把ASCII space(&#x0020;)ASCII tab(&#x0009;)ASCII form feed(&#x000C)Zero-width space(&#x200B;)纳入white space囊中,另外还将line break(换行符)carriage return(&#x000D;)line feed(&#x000A;)和它俩的组合纳入white space中。

    inter-word space——White space的用途之一

     西文是以空格来分隔单词的,而汉字间则无需空格分隔,但为了统一西文、东亚和CJK的排版,于是抽象出一个名为inter-word space的概念用于分隔词义单元,white space则作为inter-word space的值域,而定义域就是语言信息。如西文以ASCII SPACE作为inter-word space,而泰文则以Zero-width space作为inter-word space,汉语则没有inter-word space,所以word-spacing属性不影响汉字间的距离,本来无一物何处惹尘埃呢。字形、单词间的水平距离

    White space collapsing的玩法

    兼容性问题又来了,因为各浏览器的实现均不尽相同。

    <style type="text/css">
      span{background:#F60;}
    </style>
    <div><span>&#x0020;&#x000D;&#x000A;&#x000D;&#x000A;before</span></div>
    <div><span>&#x000D;&#x0020;&#x000A;&#x000D;&#x0020;&#x000A;before</span></div>
    <div><span>after&#x000A;&#x000D;&#x000A;</span></div>
    <div><span>after&#x000A;&#x000D;</span></div>
    <div><span>after&#x000A;&#x0020;</span></div>
    <div><span>one&#x000A;&#x0020;two</span></div>
    <div><span>one&#x000A;&#x0020;&#x000D;&#x000A;&#x0020;two</span></div>
    <div><span>&#x0009;&#x0020;&#x000C;&#x000D;&#x000A;</span></div>
    

    ** chrome43 **

    1. 对于起始标签与第一个non-white-space字符间的white-space字符串,以carriage return(&#x000D;)作为white-space合并单元的起始符,最后保留各合并单元的合并结果。
    2. 结束标签与最后一个non-white-space字符间的white-space字符串,以carriage return(&#x000D;)作为white-space合并单元的结束符,最后保留各合并单元的合并结果。
    3. 词义单元间的white-space字符串,以carriage return(&#x000D;)作为white-space合并单元的分界符,最后保留各合并单元的合并结果。
    4. 标签内仅包含white-space字符串,那么这些white-space字符串将被忽略。


    ** FF5.0 **

    1. 对于起始标签与第一个non-white-space字符间和结束标签与最后一个non-white-space字符间的white-space字符串将被忽略。
    2. 词义单元间的white-space字符串,以carriage return(&#x000D;)作为white-space合并单元的分界符,最后保留各合并单元的合并结果。
    3. 标签内仅包含white-space字符串,那么这些white-space字符串将被忽略。


    ** IE8+ **

    1. 对于起始标签与第一个non-white-space字符间和结束标签与最后一个non-white-space字符间的white-space字符串将被忽略。
    2. 词义单元间的white-space字符串,合并为1个(ASCII space)字符。
    3. 标签内仅包含white-space字符串,那么这些white-space字符串将被忽略。

    ** IE5.5/6/7 **

    1. 对于起始标签与第一个non-white-space字符间的white-space字符串将被忽略。
    2. 结束标签与最后一个non-white-space字符间的white-space字符串,合并为1个(ASCII space)字符。
    3. 词义单元间的white-space字符串,合并为1个(ASCII space)字符。
    4. 标签内仅包含white-space字符串,那么这些white-space字符串将被忽略。


    合并单元:合并单元包含0到N个white-space字符串,最终合并为0到1个white-space字符
     SGML描述B.3.1 Line breaks

    specifies that a line break immediately following a start tag must be ignored, as must a line break immediately before an end tag. This applies to all HTML elements without exception.

    <A>My favorite Website</A>
    <A>
    My favorite Website
    </A>
    

    望文生义翻译法:标签与正文间的line breaks要被忽略掉!也就是上下两种HTML格式的渲染效果应该一致。实际上除了IE5.5/6/7外其他浏览器均遵守之一规定的。也许你会说上面的实验不是已经证明chrome43不遵守这个法则吗?其实

    <A>
    My favorite Website
    </A>
    

    HTML格式等价于<A>#x000A;My favorite Website#x000A;</A>而不是<A>#x000D;#x000A;My favorite Website#x000D;#x000A;</A>。现在大家都清楚了吧:)
     绕到这里我想大家都有点晕了,到底这个跟问题2有啥关系呢?先不要着急嘛,我们先记住两点:

    1. IE5.5/6/7中"结束标签与最后一个non-white-space字符间的white-space字符串,合并为1个(ASCII space)字符";
    2. IE5.5/6/7中仅字符(串)可以作为词义单元,而IE8+中inline-level element也作为词义单元。
    <span class="bk1 item"></span>
    <span class="bk2 item"></span>
    <br/><br/>
    <span class="bk1 item">bk1</span>
    <span class="bk2 item"></span>
    

    IE5.5/6/7下等价于

    <span>&#x000A;</span>
    <br/><br/>
    <span>bk1&#x000A;</span>
    

    对比一下上面的规则,空隙自然就有了。
    IE8+下等价于

    <span>&nbsp;&#x000A;&nbsp;</span>
    <br/><br/>
    <span>&nbsp;&#x000A;&nbsp;</span>
    

    inline-level element整体作为词义单元,从外部看根本不用管里面具体字符串是什么。

    后来者居上——CSS2.1描述中的inline-block

     相对IE自定义的inline-block,CSS2.1引入的inline-block就好理解多了,它做了两件事:

    1. 将元素变性为inline-level element;
    2. 让元素产生新的BFC。

    消灭尾行者

     现在我们终于明白通过display:inline-block进行元素的水平排版时,为啥会有个讨人厌的跟屁虫了,那剩下的工作当然是去而快之啦。首先这个跟屁虫实质上就是white-space字符串,而我们一般会输入的就是ASCII space(&#x0020;)ASCII tab(&#x0009;)和让HTML Markup更可读的line breakscarriage return(&#x000D;)line feed(&#x000A;)
    那么消灭尾行者的方式就只有两个方向:1. 从根本上消除white-space字符串;2. 视觉效果上消除white-space字符串的影响。

    牺牲HTML Markup可读性

    牺牲前

    <span>one</span>
    <span>two</span>
    <span>three</span>
    

    牺牲后1:一行搞定(一大坨代码,会斗鸡眼的。。。)

    <span>one</span><span>two</span><span>three</span>
    

    牺牲后2:注释衔接(通过JS获取子元素数会有问题)

    <span>one</span><!--
    --><span>two</span><!--
    --><span>three</span>
    

    牺牲后3

    <span>one</span
    ><span>two</span
    ><span>three</span>
    

    然后@一丝姐说为展现效果牺牲结构是耍流氓,@HAX说这是"削足适履"。虽说这方法从根本上清除了white-space字符串,但那种丑不是一般人能接受的。

    font-size:0大法

     这种方式存在兼容性的问题,而且子元素需要重新设置font-size以保证后续采用em设置属性值正确有效这个就是一个巨蛋疼的事了。

    负margin-right法

     原理是通过负margin-right将white-space字符收入盒子后方,而margin-right的属性值需要根据font-size来决定,必须恰恰等于字形宽度的负数,否则会出现元素重叠的问题。(IE5.5/6/7不兼容这玩法)

    引入HTML预编译

     引入如Jade等HTML模板引擎,开发和维护时采用可读性可维护性更高的语言,而浏览器运行时则采用效率更佳但可读性差甚至非人类友好的编码,然后通过如sourcemap来做映射。
     但若仅仅为解决本文的问题而引入HTML模板引擎,是不是小题大造了呢?

    用float啦!

     既然上述方式皆不爽,而你又熟知float的使用和注意事项,那直接换成float就好了。float的内容可参考《CSS魔法堂:说说Float那个被埋没的志向》

    总结

     原来display:inline-block受委屈的这么多年,现在总算沉冤得雪了!都怪CSS2没有专门的布局模块,逼得我们东拼西凑地拼页面。所幸的是CSS3专设了Flexbox/Grid/Multi-columns Layout Modules,我们可以寄望更美好的将来了!
     尊重原创,转载请注明来自:http://www.cnblogs.com/fsjohnhuang/p/5396037.html_肥仔John

    感谢

    inline-block 前世今生
    inline-block 未来
    应不应该使用inline-block代替float
    inline-block元素间间隙产生及去除详解
    有哪些好方法能处理 display: inline-block 元素之间出现的空格?
    Fighting the Space Between Inline Block Elements
    拜拜了,浮动布局-基于display:inline-block的列表布局
    9.1 White space
    9.3.2 Controlling line breaks

  • 相关阅读:
    UVA 10617 Again Palindrome
    UVA 10154 Weights and Measures
    UVA 10201 Adventures in Moving Part IV
    UVA 10313 Pay the Price
    UVA 10271 Chopsticks
    Restore DB後設置指引 for maximo
    每行SQL語句加go換行
    种服务器角色所拥有的权限
    Framework X support IPV6?
    模擬DeadLock
  • 原文地址:https://www.cnblogs.com/fsjohnhuang/p/5396037.html
Copyright © 2011-2022 走看看