Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

CSS3着重符及其fallback #5

Open
zmmbreeze opened this issue Aug 31, 2016 · 0 comments
Open

CSS3着重符及其fallback #5

zmmbreeze opened this issue Aug 31, 2016 · 0 comments

Comments

@zmmbreeze
Copy link
Owner

zmmbreeze commented Aug 31, 2016

以前的旧文章,转移到这里

在东亚国家,人们会在文章中重要文字旁加上小符号以突出其重要性。如下:

ttt1

标准

在中文里面,我们一般会在文字下方加上圆形符号。在日语中会在文字上方加上小顿号。在CSS3中如下属性可以控制着重符号:

  1. text-emphasis
  2. text-emphasis-style
  3. text-emphasis-color
  4. text-emphasis-position

text-emphasistext-emphasis-styletext-emphasis-color的快捷方式,注意它并不包含text-emphasis-position

text-emphasis-style属性用于控制着重符号的样式。基本的符号形状有dot | circle | double-circle | triangle | sesame这几种,它们又分别有“空心(open)”、“填充(filled)”两种展现形式。当你只填了filledopen是,符号形状的默认值和文字是竖排还是横排(writing-modes)有关,比如横排时默认是filled circle,竖排时是filled sesame。如果你之前填了符号形状,则默认的展现形式是filled。当然你还可以自定义符号,但是只能显示一个字符。

另一个重要的属性是text-emphasis-color,它控制了着重符号的颜色,默认使用当前文字的颜色。

最后一个属性是text-emphasis-position,它有这几种可选值[ over | under ] && [ right | left ]。它的默认值计算方式更为复杂些,与横竖排版和所处语言环境都有关系。横排情况下,中文环境默认值为under,日文环境默认值为over。竖排情况下,中文和日文环境下默认值都为right

在CSS中,一般着重符号的字体大小是其对应文字的一半。且当行高有足够空间来绘制着重符时,它不会影响到对应文字的行高。

在2013年8月1日,这个标准成为“候选推荐标准”,这对喜爱文字排版的人来说是个好消息。遗憾地是目前只有webkit内核的浏览器是支持它的,并且需要使用webkit前缀。所以在使用时需要做fallback。

FALLBACK

在做fallback时,有这么几点是需要考虑的:

  1. 如何应对letter-spacing样式和文字宽度不一致的情况
  2. 如何处理浏览器的最小字体配置
  3. 如何空间是否足够绘制着重符(计算行高)
  4. 如何减少对现有html的影响
  5. 如何获得所处语言环境

对于第一点的解决方案是:对每个字符用span包裹,方法类似于letter.js。然后在每个span内部插入另一个包含着重符的span,它的宽度为百分百,且绝对定位。如下面样例所示:

<iframe src="http://jsfiddle.net/zmmbreeze/C4KaB/embedded/result,html,css" height="300" width="100%" allowfullscreen="allowfullscreen" frameborder="0"></iframe>

对于有letter-spacing的情况,可以设置span的letter-spacing为0,然后使用margin-right来替代它。

如果你是用chrome,你可能已经注意到了“最小字体”导致的问题:着重符号太大了。在chrome下着重符号是12px,而不是8px(16/2)。为了解决问题2,我们需要想想其他方法。我首先考虑到的是zoom属性,它支持chrome(所有版本)、safari和IE。可惜的是在chrome下zoom:0.5也不能使字体变小。然后考虑到的是transform:scale(0.5),幸运地是它能使文字比最小字体还要小。不过支持的浏览器不够多,参考这里。所以必须要考虑在不支持transform的时候使用fallback。我的处理方法是使用绝对大小(px)。虽然不能使着重符号字体变小,但是至少可以保证着重符位置正确。

<iframe src="http://jsfiddle.net/zmmbreeze/C4KaB/1/embedded/result,html,css" height="300" width="100%" allowfullscreen="allowfullscreen" frameborder="0"></iframe>

在绘制着重符时,如果行高内有足够的高度,则着重符不会扩大行高。如果高度不够,则扩大行高。第二种情况需要设置display:inline-block; 及padding-bottom,来模拟行高高度的扩大。为了做高度是否充足的判断,我们就需要计算字体大小和行高。当你设置字体大小为1em时,对于IE这样的浏览器,获得地长度其实并不是以px为单位。这时需要一些hack:

    // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
    var PIXEL = /^\d+(px)?$/i;
    function getPixelValue(element, value) {
        if (PIXEL.test(value)) {
            return parseInt(value);
        }
        var style = element.style.left;
        var runtimeStyle = element.runtimeStyle.left;
        element.runtimeStyle.left = element.currentStyle.left;
        element.style.left = value || 0;
        value = element.style.pixelLeft;
        element.style.left = style;
        element.runtimeStyle.left = runtimeStyle;
        return value;
    };

幸运地是,jQuery已经处理了这种情况。这样我们就可以得到正确的字体大小和行高(需要特殊处理行高为缩放因子和normal的情况)。不过受到了“文字可能比设置地字体大小更大”、“同行有更高行高的元素(例如图片)”等等特殊情况的限制,导致了计算结果并不一定正确。幸运地是对最终结果的影响并不是很大。

第四个问题指的是innerText/$('em').text()的返回值在做了fallback之后就不再正确了,同时受到影响的还有innerHTML。对于后者我没有想到好的方案。对于前者,我们可以把着重符放在span标签的before伪元素上。这样得到的innerText值还是正确的。不过也引入了另一个问题:如何用js修改before伪元素的样式。我采用的方法是插入css rule,下面有简单的代码。在实际情况下,因为不能删掉css rule,所以需要做好css rule的缓存复用。

    var styleSheet;
    function addCSSRule(selector, rules) {
        if (!styleSheet) {
            var style = document.createElement('style');
            style.type = 'text/css';
            $('head').eq(0).prepend(style);
            styleSheet = document.styleSheets[0];
        }

        if (styleSheet.insertRule) {
            styleSheet.insertRule(
                selector + '{' + rules + '}',
                styleSheet.cssRules.length
            );
        } else {
            // IE
            styleSheet.addRule(selector, rules, -1);
        }
    }

最后一个问题的解决方案则比较简单粗暴。获取navigator.language || navigator.browserLanguage的值判断其所处的语言环境。

jQuery.emphasis.js

解决了这些问题之后,终于得到了一个可用的fallback。再根据标准来修改优化代码,就得到了jQuery.emphasis.js。这里有些它的demo。不过它没有解决所有的问题,目前已知的缺陷如下:

  1. 不支持竖排(即不支持positionright/left
  2. 不支持特殊情况下的inline-block元素(比如默认元素有padding-bottom
  3. 如果浏览器不支持transform,则会有最小字体导致的着重符过大问题

如果你还发现了其他问题,欢迎feedback

# for free to join this conversation on GitHub. Already have an account? # to comment
Projects
None yet
Development

No branches or pull requests

1 participant