摘要:究竟什么样的应用场景中才需要使用CSS来隐藏文本,为什么不直接修改HTML来删除想要隐藏的内容呢?使用CSS来隐藏页面文本,究竟是如何实现的?为什么要在项目中这么做,究竟为何?究竟什么是Kellum方法,该方法的使用范围与方法的局限性是?

Abstract: What kind of application scenarios need to use CSS to hide text, why not directly modify the HTML to delete the content that you want to hide? How to use CSS to hide page text? Why do I do this in my own project? When is the Kellum method, the scope of use of the method and the limitations of the method?

引言

开篇立意,在探索全栈程序猿的路上我还坚持爬行着。在本文伊始想给大家大家讲一下,这里没有什么所谓专家和也没有能独挡一面的技术大拿,有的仅仅是一个愿意不断尝试,愿意学习的学生而已。不忘初心,正所谓阅文无数,不如明师指路;明师指路,不如自己去悟。说到底,学习知识做到知行合一才是真理。

任务背景

为了学会CSS相关的内容,更为了把学到的东西用到实处,在实践中发现问题,再回过头来继续学习,螺旋上升,加快成长。从今天开始,所发的CSS相关的文章就是在记录自己学习使用CSS的时候走的每一步。

为了学习前端知识,在过去一段时间尝试完成的项目是一个管理构建项目和查看构建状态的网页工具,在该项目中使用了Material Design Lite组件用于解决页面布局和基本的元素样式。使这个该组建也是为了学习和了解Material Design这种扁平化设计理念。官方网页介绍的使用方法如下,是在html页面中导入CSS和JavaScript两者资源。

1
2
3
<link rel="stylesheet" href="./material.min.css">
<script src="./material.min.js"></script>
<link rel="stylesheet" href="https://fonts.googleapis.com/icon?family=Material+Icons">

其中material.min.css文件,是自己在https://getmdl.io/customize 官方页面下,按照说明文档,自定义生成的,其使用的默认主色为indigo和强调色为pink。相关的使用方法与规范,请参见官方文档,本文不再做说明。

所遇问题

有关Material Design Lite使用方法的内容中关于字体图标Material+Icons的链接的部分,由于大家熟知的原因,使用这个地址的CDN是不被推荐的,另一方面,因为该小型管理项目最终也是在内部网络使用的,再者初始版本中使用的是w3 css开源主题,Roboto免费字体与Font Awesome开源图表库,这些内容也希望能新的样式中继续使用。所以类似这样的外部链接就有悖项目现实情况。当然网络中也有此类资源本地使用方法的介绍,相关内容因与本文无关,不在赘述。

自己项目页面基本布局如下图所示,左侧为导航navibar部分,底部为页脚footer部分,右上为页眉header部分与右侧中部为主要内容显示部分。navibar会根据页面宽度自动显示与隐藏,这是Material Design Lite的组件特性。

图例2

项目中header部分的代码如下,注意,这里使用的是jade模版。

1
2
3
4
5
6
// header part
header(class="mdl-layout__header")
div(class="mdl-layout__header-row")
span(class="mdl-layout-title").
#{t('index.title', 'My header title')}
div(class="mdl-layout-spacer")

由于是响应式的UI布局,当页面尺寸低于一个值时,左侧导航菜单就会被隐藏,同时按钮图标会显示在Header部分,估计很多朋友都见过类似的响应式布局,如此一来,就遇到了本文所述的关键问题。前文说道项目中没有引入Material+Icons的链接。也就无法显示正确显示这个按钮,具体情况如图所示:

图例3

该按钮是由material.min.js自动生成的,当页面宽度缩小到一定值时,页眉header部分的代码变为如下所示,对比之间的代码可见在div(class=”mdl-layout__header-row”)上面出现了一个新的div块,内部为一个图标元素,该图标即为上图中的菜单按钮,由于定义图标的资源未被引入,所以图标未正确显示。

1
2
3
4
5
6
7
<header class="mdl-layout__header is-casting-shadow">
<div aria-expanded="false" role="button" tabindex="0" class="mdl-layout__drawer-button">
<i class="material-icons"></i>
</div>
<div class="mdl-layout__header-row">
<span class="mdl-layout-title">My header title</span>
...

按钮图标的内容content是由常量MaterialLayout.prototype.Constant_中的MENU_ICON来定义的,可以通过查看未压缩的material.js文档找到如下的定义:

1
2
3
4
5
6
7
8
MaterialLayout.prototype.Constant_ = {
MAX_WIDTH: '(max-width: 1024px)',
TAB_SCROLL_PIXELS: 100,
RESIZE_TIMEOUT: 100,
MENU_ICON: '&#xE5D2;', # 按钮图标的值
CHEVRON_LEFT: 'chevron_left',
CHEVRON_RIGHT: 'chevron_right'
};

解决方案

由于项目现实条件约束,不能引入Material+Icons的链接,又因为最后使用的是压缩后的min.js文件,文件可读性太差,再者修改代码就要理解机制,所以修改相应的js代码也不现实。那能不能通过CSS控制元素的样式隐藏这个图标内容呢?这就是今天文章的主要内容。使用CSS来隐藏页面文本元素的方法很简单,关键代码如下:

1
2
3
4
5
.hide-text {
text-indent: 100%;
white-space: nowrap;
overflow: hidden;
}

实验表明,该方法适用于现有项目,解决了所遇问题,再使用的时候我就好奇背后的原理。也想知道上述CSS代码是如何实现隐藏文本的呢?背后逻辑与思想究竟又是什么?(诸位前辈莫笑,毕竟本人CSS理论知识不足,大部分就是现学现用的)。结合上述方法和项目本身条件,由于隐藏该内容的目的是想使用新的图标,所以,最终相关的CSS代码如下,由于文本文档被隐藏,这时使用新的按钮图标<i>来替换原始按钮图标也会被隐藏,所以就需要以背景的方式显示新的图标,由于Font Awesome图标的颜色为黑色,为此我还修改了相应svg文件中的“fill”属性,使其图标颜色为白色。

1
2
3
4
5
6
7
8
9
10
11
12
13
.mdl-layout__header .mdl-layout__drawer-button .material-icons {
background-size: 30px 30px;
background: url(../fonts/bars-solid.svg) no-repeat;
width: 30px;
font-size: 30px;
color: white;
display: block;
margin: auto;
margin-top: 0.3em;
text-indent: 100%;
white-space: nowrap;
overflow: hidden;
}

这个方法被称为为Kellum方法,在方法溯源上我没有去深究。在搜索外文资料时,输入该关键字确实能够过滤结果。该方法主要思想就是在设置文中不换行white-space: nowrap的前提下,无限制地增加首行缩进text-indent: 100%从而将文本内容推出到页面的显示范围。

1
overflow: hidden

该方法内overflow: hidden的使用算是非常聪明的地方,也说明了该方法设计者对CSS的经验之丰富。测试表明,如果在该方法中不使用overflow:hidden,那么如果文本中包含<a>元素的话,左侧的边界会被击穿,即文本中的第一个字符还是会被显示出来,感兴趣可以测试下下列代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<html>
<head>
<style type="text/css">
p {
text-indent:100%;
white-space: nowrap;
//overflow:hidden
}
</style>
</head>
<body>
<p>122345678abcdefghijklmnopqrstuvwxy
<a href="">link</a>
</p>
</body>
</html>

对该方法的讨论还远不止如此,有人建议使用“text-indent: 9999px”来替代“text-indent:100%”从而保证文本内容一定会被推出显示范围,而且如果使用9999px的话,可以不需结合overflow:hidden,此时文本的左侧边界不会被击穿。感兴趣的话,也可以结合上面的示例页面代码自己尝试一下。但也有一部分人对“9999px”在该方法中替代“100%”持反对态度,因为如果使用绝对值的话,浏览器就需要画出9999px边框,这样的话在性能方面就会有些损失。

也有人在使用时发现,该方法并不适用于html的<button>元素,使用此方法时,Button元素的右侧,即带有缩进的文本的左侧边界也会被击穿,此时情况与上文中文本包含<a>元素时一致,可以使用下方代码进行测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<html>
<head>
<style type="text/css">
button
{
width: 120px;
height: 32px;
text-indent: 100%;
white-space: nowrap;
overflow: hidden
}
</style>
</head>
<body>
<button>Button</button>
</body>
</html>

上述代码的结果如下,可以看出文本左侧边界被“击穿”了。

图例1

为什么遇到<button>元素时,即便结合使用了overflow: hidden还是会出现页面被击穿的现象呢?这是因为一些类似于<button>的html元素本身自带内部填充intrinsic padding。重置button的padding属性就可以解决这一问题,当然将text-indent的值设置为大于100%,或使用前文中所说的9999px也能达到相似的结果。将下面的代码添加至上面的html代码中就能解决这个问题:

1
2
3
4
* {
box-sizing: border-box;
margin: 0; padding: 0;
}

小结

本人在CSS方面的知识并不丰富,一直在不断学习,文章中所述所写必然存在不足之处,望各位读者多多指正。如果哪位朋友有更好的解决方案,更希望能不吝赐教,大家一同学习,共同进步。欢迎关注我的微信公众号,留言讨论。

参考目录

Hide text using css: https://stackoverflow.com/questions/471510/hide-text-using-css

why the kellum method doesn’t work with button elements? : https://stackoverflow.com/questions/26992552/css-image-replacement-why-the-kellum-method-doesnt-work-with-button-elements

CSS white-space 属性: https://www.w3school.com.cn/cssref/pr_text_white-space.asp

CSS text-indent 属性: https://www.w3school.com.cn/cssref/pr_text_text-indent.asp

Replacing the -9999px hack (new image replacement) : http://www.zeldman.com/2012/03/01/replacing-the-9999px-hack-new-image-replacement/


版权声明:
文章首发于 Jim Wang's blog , 转载文章请务必以超链接形式标明文章出处,作者信息及本版权声明。