前端开发中的一些黑魔法Pt1

编辑推荐: 掘金是一个高质量的技术社区,从 CSS 到 Vue.js,性能优化到开源类库,让你不错过前端开发的每一个技术干货。 点击链接查看最新前端内容,或到各大应用市场搜索「 掘金」下载APP,技术干货尽在掌握中。

@Vitaly Friedman在今年3月份分享了一个主题《Dirty Tricks From The Dark Corners Of Front-End》。整个PPT中介绍了一些前端开发中使用到的一些黑魔法,当初看的时候特别的带劲。一直想将里面的内容整理出一篇文章出来,但一直没整,趁最近休假的闲暇时间整理了一下。

如果你的英文够好,可以直接听原作者分享的视频:

对应的PPT:

当然,如果您愿意花时间阅读下面的内容,我会感到非常的开心。同时我也希望下面的内容对您有在平时的工作当中有所帮助。

链接嵌套

众所周之,在前端开发当中,超链接<a>标签嵌套<a>标签会出问题:浏览器解析的时候,将它们解析成不嵌套的。来看代码:

<a href="#url1">When the crisis was over,  <a href="#url2">Mr. Jones</a> left the region immediately.</a>

浏览器解析上面的HTML代码将会变成这样:

<a href="#url1">When the crisis was over,</a>  
<a href="#url2">Mr. Jones</a> 
left the region immediately.

如下图所示:

链接嵌套

而我们事实上需要的达到的效果是:

<a href="#url1">When the crisis was over,</a>  
<a href="#url2">Mr. Jones</a> 
<a href="#url1">left the region immediately.</a>

曾经为了达到这样的效果,我只能这样老实的写HTML代码,那么事实上呢?我们是存在一定的黑魔法的。那就是给<a>标签内部的<a>标签外添加一个<object>标签。如下所示:

<a href="#url1">When the crisis was over,  <object><a href="#url2">Mr. Jones</a></object> left the region immediately.</a>

最终浏览器达到的效果其实和下面的HTML代码效果一样:

<a href="#url1">When the crisis was over,</a> 
<a href="#url2">Mr. Jones</a> 
<a href="#url1">left the region immediately.</a>

当然,其结构未做任何改变,但效果是类似上面的代码效果。

链接嵌套

在线效果:

上面的效果在IE9+、Firefox4+、Opera9+、Safari5.1+、Chrome14+都可以完整运行,如果在老板版本IE浏览器上要有效果的话,需要在<object>标签内添加一个注释。

破裂的图片

默认情况之下,图片加载未成功的话会有一个破裂的图片,直接影响页面的美观。那么有没有什么方法可以通过样式让未加载的图片看起来真的像是破裂的效果?

破裂的图片

<img>元素加载的是外部图片资源,未加载成功时,在页面上就会显示类似上图的效果。<img>是一个替代元素,其外观和尺寸都是由外部资源来决定的。由于元素的内容是外部资源,那么可以通过CSS的伪元素::before::aftercontent中调用attr()函数,并且给arrt()<img>src值。如此一来,就可以很轻松的控制图片破裂的效果。

img { /* Same as first example */ }

img:after {  
  content: "\f1c5" " " attr(alt);

  font-size: 16px;
  font-family: FontAwesome;
  color: rgb(100, 100, 100);

  display: block;
  position: absolute;
  z-index: 2;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: #fff;
}

这是默认的一个替换效果:

破裂的图片

在上面的效果基础上,还可以做一些样式扩展:

img {  
  /* Same as first example */
  min-height: 50px;
}

img:before {  
  content: " ";
  display: block;

  position: absolute;
  top: -10px;
  left: 0;
  height: calc(100% + 10px);
  width: 100%;
  background-color: rgb(230, 230, 230);
  border: 2px dotted rgb(200, 200, 200);
  border-radius: 5px;
}

img:after {  
  content: "\f127" " Broken Image of " attr(alt);
  display: block;
  font-size: 16px;
  font-style: normal;
  font-family: FontAwesome;
  color: rgb(100, 100, 100);

  position: absolute;
  top: 5px;
  left: 0;
  width: 100%;
  text-align: center;
}

破裂的图片

浏览器支持效果如下表所示:

破裂的图片

有关于图片破裂的美化,@ireaderinokun专门写了一篇文章《Styling Broken Images》。详细介绍了整个实现原理与过程。

表格行和列高亮显示

表格行和列高亮显示,比如下图效果:

表格行和列高亮显示

上图演示的是鼠标在表格顶部或行的左边缘能让当前列或行高亮显示,如果在中间能让当前行和列同时高亮。感觉要通过tr:hover实现这样的效果是件非常蛋疼的事情。因为其只能实现当前行高亮显示,但无法让当前列也高亮。还好@CHRIS COYIER在《Simple CSS-Only Row and Column Highlighting》一文 中提供了一种黑魔法。

通过td:hover配合::before::after,并且将其高度设置无限高,同时改变::after::beforez-index值。重要的一点,需要给table设置一个overflow:hidden,将伪元素溢出的高度截取掉。

table {
    overflow: hidden;
} 
td, th {
    position: relative; 
}  
tr:hover {
    background-color: #ffa;
}  
td:hover::after {
    content: ""; 
    position: absolute;  
    width: 100%;  
    height: 10000px;  
    left: 0; 
    top: -5000px; 
    background-color: currentColor;  
    z-index: -1; 
}

效果如下:

表格行和列高亮显示

对上面的样式再进行改良一下:

table { 
    overflow: hidden; 
} 
tr:hover { 
    background-color: #ffa; 
}  
td:hover::after, 
th:hover::after { 
    content: ""; 
    position: absolute;  
    width: 100%;  
    height: 10000px;  
    left: 0; 
    top: -5000px; 
    background-color: currentColor;  
    z-index: -1; 
}

表格行和列高亮显示

一起看一个在线示例:

内联文本设置背景色

如果你想给一个内联标题文本添加背景颜色。如果文本是多行的话,你只能在第一行我的左边和最后一行的右边有一定的填充距离,其它行的填充都直接紧挨着左右边缘,效果如下:

内联文本设置背景色

而要让每行的背景色距离文本左右内距相等,实现方法有很多种。@CHRIS COYIER早在2013年的一篇文章《Multi-Line Padded Text》就整理了几种方法。

@Harry Robert通过伪元素配合white-space: pre-wrap来实现:

@Fabien Doiron通过box-shadow方法,给行内元素添的x轴方向添加阴影:

.padded-multi-line {
  display: inline;
  background: orange;
  box-shadow: 10px 0 0 orange, -10px 0 0 orange;
}

效果如下:

@Adam Campbell使用了CSS的一个新属性box-decoration-break

除此之外,还可以使用CSS3的gradient来实现:

a {
  font-size: 4rem;
  text-decoration: none;
  color: #1e1f23;
  text-shadow: 1px 1px 0 rgba(255,255,255,0.4);
}
.fun-hover {
  background-image: -webkit-linear-gradient(left, #25b0a9 50%, #fee603 50%);
  background-image: linear-gradient(to right, #25b0a9 50%, #fee603 50%);
  background-position: 0;
  background-size: 200%;
  -webkit-transition: all 0.4s;
  transition: all 0.4s;
}
.fun-hover:hover {
  background-position: -100%;
}

效果如下:

响应式EDM

很多邮件客户端是不支持媒体查询的,比如Gmail。以前一直使用table的布局来实现响应式EDM的设计,那么有没有其它更好的方式来实现?

EDM设计中很多时候内容都是依赖于图片堆积而来,这样也就造成了,用户在邮件客户端查阅邮件时,图片未加载成功,此时你的很多信息无法友好的展现给你的用户,如下图所示:

响应式EDM

如果我们在制作EDM时,将图片的alt美化一下,并且给图片所占区域加上一些background-color样式。就算图片未显示,是不是向用户展示的EDM也会更佳友好一些。如下图所示:

响应式EDM

现来看一个简单的实例,假设要做一个类似下图的效果:

响应式EDM

左边是在宽屏下显示的效果,右边是在窄屏下显示的效果。

<table> /* “Desktop” width = 600px = 300*2 */   
    <tr> 
        <td class="col" width="300">...</td> 
        <td class="col" width="300">...</td>  
    </tr> 
</table> 

采用媒体查询在窄屏下:

@media only screen and (max-width: 600px) {  
    table, tr, td { 
        display: block; /* table-cell -> block */ 
        width: 100%;  
    } 
} 

再来看一个列的切换:

响应式EDM

<table> /* “Desktop” width = 600px = 300*2 */   
    <tr> 
        <td class="sub-col" width="300">...</td> 
        <td class="main-col" width="300">...</td>  
    </tr> 
</table> 

@media only screen and (max-width: 500px) {  
    table, tr, td { 
        display: block; /* table-cell -> block */ 
        width: 100%;  
    } 

    td[class=main-col] { 
        display: table-header-group; 
    }  
    td[class=sub-col] { 
        display: table-footer-group; 
    } 
} 

很多时候EDM会涉及到响应式图片的处理与布局:

响应式EDM

<table> /* “Desktop” width = 600px */   
    <tr> 
        <td class="image" width="100">...</td>  
        <td class="content"> 
            <div class="header">...</div> 
            <div class="description">...</div> 
        </td> 
    </tr>  
</table>

@media only screen and (max-width: 500px) {  
    table, tr, td { 
        display: block; 
    }  
    td[class=image] { 
        float: left; 
    }  
    .description { 
        clear: both; 
    } 
} 

根据不同的终端改变排列顺序也是常有的需求:

响应式EDM

<td class="wrapper"> /* Nested tables, oh my... */   
    <table class="header">Header</table> 
    <table class="nav">Navigation</table> 
    <table class="content">Content</table> 
    <table class="footer">Footer</table>
</td>

@media only screen and (max-width: 500px) {  
    table[class=wrapper] { 
        display: table; 
    }  
    table[class=header] { 
        display: table-caption; 
    }  
    table[class=nav] { 
        display: block; 
    }  
    table[class=content] { 
        display: table-header-group; 
    }  
    table[class=footer] { 
        display: table-footer-group; 
    } 
} 

除此之外,calc()widthmin-widthmax-width的配合有做出更好的效果。

响应式EDM

.box{
    width: 320px;
    min-width: 480px;
    max-width: 160px;
}
  • 如果width的值大于max-width的值,将会取max-width的值:
  • 如果min-width的值大于widthmax-width的值,将会取min-width的值。

在不使用媒体查询情况之下,屏幕小于480px,采用两列布局:

.box { 
    display: inline-block; 
    min-width: 50%; // basically 2-col-desktop version  
    max-width: 100%; // basically 1-col-mobile version  
    width: calc((480px - 100%) * 480); 
    /* 480px = the breakpoint, 100% = width of the parent  Goal: create a value bigger than our max-width or smaller than our min-width, so that either one of those property is applied instead. */ 
}

看看实际推进采用的代码:

响应式EDM

响应式EDM

实际效果如下:

早在2013年时花了不少时间致力于响应式EDM的设计,如果您对这方面知识感兴趣,欢迎阅读早期整理过的一些文档

扩展阅读

长单词处理

网页中会用到很多种语言,而有些语言的单词很长,那么对于这样的长单词,面对众多的设备需怎么处理,让其更为合理。

长单词处理

我们更想要的效果是:

长单词处理

@Michael Scharnagl在《Dealing with long words in CSS》中提供了一个最终方案:

.hyphenate {
    overflow-wrap: break-word;
    word-wrap: break-word;
    hyphens: auto;
}

处理CSS方式

处理CSS方式是令人讨厌的,需要正确的维护,而且不能被缓存。那么有没有更好的方法处理CSS?有人提出关键CSS的加载(Deploying Critical CSS):

  • 在HTTP/2时代,HTTP的请求是便宜的,将CSS分割成很多个小的文件
  • 在HTTP/2的世界中,内联CSS是一个很重的开销。系列加载CSS文件时,服务器加载缓慢

我们加载CSS的方式是这样:

<head> 
    <link rel="stylesheet" href="combined.min.css"> 
</head>  
<body>  
    ...content... 
</body>

但我们应该开始远离这样的加载方式。将采用下面的加载方式:

<head> 
    <meta name="fullcss" content="full.css"> 
    <!-- #if expr="$HTTP_COOKIE=/fullcss\=true/" -->  
        <link rel="stylesheet" href="full.css"> 
    <!-- #else --> 
        <style> /* Critical CSS styles */ </style> 
        <noscript>
            <link rel="stylesheet" href="full.css">
        </noscript> 
    <!-- #endif -->  
</head>

然后是这样:

<head> 
    <!-- #if expr="$HTTP_COOKIE=/fullcss\=true/" --> 
        <link rel="stylesheet" href="full.css">  
    <!-- #else --> 
        <style> 
            /* Critical CSS styles, plus: */ 
            article, .comments, aside, footer { display: none; }  
        </style> 
        <script> 
            loadCSS("full.css"); /* or rest.css + critical.css */ 
        </script> 
        <noscript>
            <link rel="stylesheet" href="full.css">
        </noscript>  
    <!-- #endif --> 
</head>

推荐HTTP/2一个简单方式:

<head> 
    <link rel="stylesheet" href="site-header.css">  
    <link rel="stylesheet" href="article.css">  
    <link rel="stylesheet" href="comments.css">  
    <link rel="stylesheet" href="sidebar.css">  
    <link rel="stylesheet" href="site-footer.css"> 
</head>  
<body>  
    ...content... 
</body>

但是Progressive CSS方式会更好一些:

<head>...</head> 
<body> 
    <!-- HTTP/2 push critical or inline; whatever faster -->  
    <link rel="stylesheet" href="site-header.css">  
    <header>...</header> 

    <link rel="stylesheet" href="article.css">  
    <main>...</main> 

    <link rel="stylesheet" href="comments.css">  
    <section class="comments">...</section>  
    ...conten
</body>

有关于这方面相关的知识,偶了今天是初次接触,如果你想了解的更深,建议阅读@Dean Hume在2015年写的《Understanding Critical CSS》一文或者阅读@Wladston Ferreira Filho的《Annotating Your (Critical) CSS》一文。

除此之外,@Jake在《The future of loading CSS》一文中畅想了未来CSS加载的方式。如果你感兴趣的话可以仔细阅读一下。中文可以移步此处阅读

表格等宽列

默认情况之下,如果你不知道表格单元格里的内容的话,你是无法预测到其宽度,很多时候超长的内容会破坏整体的布局。

表格等宽列

解决方法很简单:

table {
  table-layout: fixed;
}

比如下面的效果:

扩展阅读

图片自适应

你已经创建好了一个完美的网格以及完美的缩略图(比如一个正方形),但当客户上传的图片尺寸不正确的时候,图片被挤压变形。此时又该如何处理这个问题呢?

比如上传的原始图如下所示:

图片自适应

为了让图片变成正方形,给图片一些基本样式:

img {
    width: 200px;
    height: 200px;
    border: 1px solid;
    background-color: #eee;
}

此时缩略图看起来像这样:

图片自适应

那么有没有更好的方案处理吗?如果图片不是<img>,而是一张背景图片,大家都知道,实现方案有很多种,比如说backgound-size。而对于img标签,咱们可以使用CSS3的一个新属性object-fit,让图片根据容器比例进行裁剪。

例如:

img {
    object-fit: contain;
}

效果如下:

图片自适应

img {
    object-fit: cover;
}

图片自适应

img {
    object-fit: none;
}

图片自适应

如果将object-fit配合object-postion使用效果会更佳。有关于object-fix相关的文档可以点击这里阅读

待续...

这份PPT涉及到的信息量非常的大,这篇文章只介绍了其中的一部分(九个),剩下的还有很多,比如Flexbox实现的一些效果、Viewport单位(vwvhvminvmax)、CSS中的一些关键词,比如inheritcurrentColor。Animation中的steps()、CSS中的混合模式以及无障碍信息相关的。如果你对这些也感兴趣,你可以直接先阅读文章开头提供的PPT或者视频,如果你想了解更详细的,欢迎持续关注后续的相关更新。

大漠

常用昵称“大漠”,W3CPlus创始人,目前就职于手淘。对HTML5、CSS3和Sass等前端脚本语言有非常深入的认识和丰富的实践经验,尤其专注对CSS3的研究,是国内最早研究和使用CSS3技术的一批人。CSS3、Sass和Drupal中国布道者。2014年出版《图解CSS3:核心技术与案例实战》。

如需转载,烦请注明出处:http://www.w3cplus.com/css/dirty-tricks-dark-corners-front-end-pt1.html

返回顶部