神奇的选择器 :focus-within
CSS 的伪类选择器和伪元素选择器,让 CSS 有了更为强大的功能。
伪类大家听的多了,伪元素可能听到的不是那么频繁,其实 CSS 对这两个是有区分的。
有个错误有必要每次讲到伪类都提一下,有时你会发现伪类元素使用了两个冒号 (::) 而不是一个冒号 (:),这是 CSS3 规范中的一部分要求,目的是为了区分伪类和伪元素,大多数浏览器都支持下面这两种表示方式。
通常而言,
#id:after{ ... } #id::after{ ... }
符合标准而言,单冒号(:)用于 CSS3 伪类,双冒号(::)用于 CSS3 伪元素。
当然,也有例外,对于 CSS2 中已经有的伪元素,例如 :before,单冒号和双冒号的写法 ::before 作用是一样的。
所以,如果你的网站只需要兼容 webkit、firefox、opera 等浏览器或者是移动端页面,建议对于伪元素采用双冒号的写法,如果不得不兼容低版本 IE 浏览器,还是用 CSS2 的单冒号写法比较安全。
伪类选择器 :focus-within
言归正传,今天要说的就是:focus-within 伪类选择器。
它表示一个元素获得焦点,或,该元素的后代元素获得焦点。划重点,它或它的后代获得焦点。
这也就意味着,它或它的后代获得焦点,都可以触发 :focus-within。
:focus-within 的冒泡性
这个属性有点类似 Javascript 的事件冒泡,从可获焦元素开始一直冒泡到根元素 html,都可以接收触发 :focus-within 事件,类似下面这个简单的例子这样:
<div class="g-father"> <div class="g-children"> <input type="button" value="Button"> </div> </div>
html, body, .g-father, .g-children { padding: 30px; border:1px solid #999; } input { ... &:focus { background: #00bcd4; } } html:focus-within { background: #e91e63; } body:focus-within { background: #ff5722; } .g-father:focus-within { background: #ffeb3b; } .g-children:focus-within { background: #4caf50; }
就是这样:
CodePen Demo -- :focus-within 冒泡触发
这个选择器的存在,让 CSS 有了进一步的让元素持久停留在一种新状态的的能力。
下面几个例子,看看 :focus-within 可以提供什么能力,做些什么事情。
感应用户聚焦区域
它或它的后代获得焦点,这一点使得让感知获焦区域变得更大,所以,最常规的用法就是使用 :focus-within 感应用户操作聚焦区域,高亮提醒。
下面的效果没有任何 JS 代码:
这里是什么意思呢?:focus-within 做了什么呢?
- 我们无须去给获焦的元素设置 :focus 伪类,而是可以给需要的父元素设置,这样当元素获焦时,我可以一并控制它的父元素的样式
核心思想用 CSS 代码表达出来大概是这样:
<div class="g-container"> <div class="g-username"> <input type="text" placeholder="user name" class="g_input" > </div> <div class="g-username"> <input type="text" placeholder="code" class="g_input" > </div> </div>
.g-container:focus-within { ... input { .... } }
DEMO -- CSS focus-within INPUT
运用上面思想,我们可以把效果做的更炫一点点,在某些场景制作一些增强用户体验的效果:
DEMO -- PURE CSS FOCUS By :focus-within
TAB导航切换
在之前的一篇文章里,介绍了两种纯 CSS 实现的 TAB 导航栏切换方法:
现在又多了一种方式,利用了 :focus-within 可以在父节点获取元素获得焦点的特性,实现的TAB导航切换:
DEMO -- focus-within switch tab
主要的思路就是通过获焦态来控制其他选择器,以及最重要的是利用了父级的 :not(:focus-within) 来设置默认样式:
.nav-box:not(:focus-within) { // 默认样式 } .nav-A:focus-within ~ .content-box .content-A { display: block; } .nav-B:focus-within ~ .content-box .content-B { display: block; }
配合 :placeholder-shown 伪类实现表单效果
:focus-within 一个人能力有限,通常也会配合其他伪类实现一些不错的效果。这里要再简单介绍的是另外一个有意思的伪类 :placeholder-shown。
:placeholder-shown:The :placeholder-shown CSS pseudo-class represents any or element that is currently displaying placeholder text.</div><div><span>另外,划重点,这个伪类是仍处于实验室的方案。也就是未纳入标准,当然我们的目的是探寻有意思的 CSS 。</span></div></blockquote><div style="text-align: left; background-color: #F5F5F5;"><span>意思大概就是,当 <code>input</code> 类型标签使用了 placeholder 属性有了默认占位的文字,会触发此伪类样式。配合<code>:not()</code>伪类,可以再改变当默认文字消失后的样式,再配合本文的主角,我们可以实现表单的一系列效果。</span></div><div style="text-align: left; background-color: #F5F5F5;"><span>CSS 代码大概呈现成这样:</span></div><div data-card-type="block" data-ready-card="codeblock" data-card-value="data:%7B%22mode%22%3A%22plain%22%2C%22code%22%3A%22.g-container%20%7B%5Cn%20%20%20%20width%3A%20500px%3B%5Cn%20%20%20%20height%3A%2060px%3B%5Cn%20%5Cn%20%20%20%20input%20%7B%5Cn%20%20%20%20%20%20%20%20height%3A%20100%25%3B%5Cn%20%20%20%20%20%20%20%20width%3A%20100%25%3B%5Cn%20%5Cn%20%20%20%20%20%20%20%20%26%3Anot(%3Aplaceholder-shown)%20%7B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20...%5Cn%20%20%20%20%20%20%20%20%7D%5Cn%20%5Cn%20%20%20%20%20%20%20%20%26%3Aplaceholder-shown%20%7B%5Cn%20%20%20%20%20%20%20%20%20%20%20%20...%5Cn%20%20%20%20%20%20%20%20%7D%5Cn%20%20%20%20%7D%5Cn%20%5Cn%20%20%20%20%26%3Afocus-within%20%7B%5Cn%20%20%20%20%20%20%20%20...%5Cn%20%20%20%20%7D%5Cn%7D%22%2C%22heightLimit%22%3Atrue%2C%22margin%22%3Atrue%2C%22id%22%3A%22jScAT%22%7D"></div><div><span style="color: #000000; background-color: #F5F5F5;">实际效果如下:</span></div><div><span style="color: #000000; background-color: #F5F5F5;"><span data-card-type="inline" data-ready-card="image" data-card-value="data:%7B%22src%22%3A%22https%3A%2F%2Fucc.alicdn.com%2Fpic%2Fdeveloper-ecology%2Feed4b0c3c51540dcb7fceb9d9d709164.png%22%2C%22originWidth%22%3A741%2C%22originHeight%22%3A106%2C%22name%22%3A%22image.png%22%2C%22size%22%3A41346%2C%22display%22%3A%22inline%22%2C%22align%22%3A%22left%22%2C%22linkTarget%22%3A%22_blank%22%2C%22status%22%3A%22done%22%2C%22style%22%3A%22none%22%2C%22search%22%3A%22%22%2C%22margin%22%3A%7B%22top%22%3Atrue%2C%22bottom%22%3Atrue%7D%2C%22width%22%3A741%2C%22height%22%3A106%7D"></span></span></div><div style="text-align: left; background-color: #F5F5F5;"><span>可以看到,上面的效果没有用到任何 JS,可以实现:</span></div><ol style="text-align: left; background-color: #F5F5F5;"><li><span>整个 input(包括父元素所在区域)获焦与非获焦样式控制</span></li><li><span>placeholder 属性设置的文字出现与消失后样式控制</span></li></ol><div><span><span data-card-type="inline" data-ready-card="image" data-card-value="data:%7B%22src%22%3A%22https%3A%2F%2Fucc.alicdn.com%2Fpic%2Fdeveloper-ecology%2Ff3d1bcf129014baa949b8f2fe3535cf2.png%22%2C%22originWidth%22%3A403%2C%22originHeight%22%3A333%2C%22name%22%3A%22image.png%22%2C%22size%22%3A20457%2C%22display%22%3A%22inline%22%2C%22align%22%3A%22left%22%2C%22linkTarget%22%3A%22_blank%22%2C%22status%22%3A%22done%22%2C%22style%22%3A%22none%22%2C%22search%22%3A%22%22%2C%22margin%22%3A%7B%22top%22%3Atrue%2C%22bottom%22%3Atrue%7D%2C%22width%22%3A403%2C%22height%22%3A333%7D"></span></span></div><div style="text-align: left; background-color: #F5F5F5;"><span><a href="https://codepen.io/Chokcoco/pen/xJWwyB" target="_blank">CodePen Demo -- :placeholder-shown && :focus-within</a></span></div><div style="text-align: left; background-color: #F5F5F5;"><span class="lake-fontsize-9"> </span></div><h2 id="koePo" style="text-align: left; background-color: #F5F5F5;"><span class="lake-fontsize-18">实现离屏导航</span></h2><div style="text-align: left; background-color: #F5F5F5;"><span>这个是其他很多文章都有提到过的一个功能,利用 <code>focus-within</code> 便捷的实现离屏导航,可以说将这个属性的功能发挥的淋漓尽致,这里我直接贴一个 codepen 上 <a href="https://codepen.io/dannievinther/" target="_blank">Dannie Vinther</a> 对这个效果的实现方案:</span></div><div style="text-align: left; background-color: #F5F5F5;"><span><span data-card-type="inline" data-ready-card="image" data-card-value="data:%7B%22src%22%3A%22https%3A%2F%2Fucc.alicdn.com%2Fpic%2Fdeveloper-ecology%2Fa76cb58a97b24ed6a1530d70ff2fce30.png%22%2C%22originWidth%22%3A747%2C%22originHeight%22%3A361%2C%22name%22%3A%22image.png%22%2C%22size%22%3A426102%2C%22display%22%3A%22inline%22%2C%22align%22%3A%22left%22%2C%22linkTarget%22%3A%22_blank%22%2C%22status%22%3A%22done%22%2C%22style%22%3A%22none%22%2C%22search%22%3A%22%22%2C%22margin%22%3A%7B%22top%22%3Atrue%2C%22bottom%22%3Atrue%7D%2C%22width%22%3A747%2C%22height%22%3A361%7D"></span></span></div><div style="text-align: left; background-color: #F5F5F5;"><span><a href="https://codepen.io/dannievinther/pen/NvZjvz" target="_blank">CodePen Demo -- Off-screen nav with :focus-within [PURE CSS]</a></span></div><div style="text-align: left; background-color: #F5F5F5;"><span class="lake-fontsize-9"> </span></div><h2 id="QfMto" style="text-align: left; background-color: #F5F5F5;"><span class="lake-fontsize-18">实现掘金登录动效切换</span></h2><div style="text-align: left; background-color: #F5F5F5;"><span><a href="http://juejin.im/" target="_blank">juejin.im</a>是我很喜欢的一个博客网站,它的登录有一个小彩蛋,最上面的熊猫在你输入帐号密码的时候会有不同的状态,效果如下:</span></div><div style="text-align: left; background-color: #F5F5F5;"><span><span data-card-type="inline" data-ready-card="image" data-card-value="data:%7B%22src%22%3A%22https%3A%2F%2Fucc.alicdn.com%2Fpic%2Fdeveloper-ecology%2F54d9b9e699be48baba7d8309469cf930.png%22%2C%22originWidth%22%3A397%2C%22originHeight%22%3A570%2C%22name%22%3A%22image.png%22%2C%22size%22%3A135317%2C%22display%22%3A%22inline%22%2C%22align%22%3A%22left%22%2C%22linkTarget%22%3A%22_blank%22%2C%22status%22%3A%22done%22%2C%22style%22%3A%22none%22%2C%22search%22%3A%22%22%2C%22margin%22%3A%7B%22top%22%3Atrue%2C%22bottom%22%3Atrue%7D%2C%22width%22%3A397%2C%22height%22%3A570%7D"></span></span></div><div style="text-align: left; background-color: #F5F5F5;"><span style="color: #000000; background-color: #F5F5F5;">利用本文所讲的 </span><code>focus-within</code><span style="color: #000000; background-color: #F5F5F5;"> ,可以不借助任何 Javascript,实现这个动效:</span></div><div style="text-align: left; background-color: #F5F5F5;"><span style="color: #000000; background-color: #F5F5F5;"><span data-card-type="inline" data-ready-card="image" data-card-value="data:%7B%22src%22%3A%22https%3A%2F%2Fucc.alicdn.com%2Fpic%2Fdeveloper-ecology%2Fe038e9f8174e413988169f381c31868d.png%22%2C%22originWidth%22%3A407%2C%22originHeight%22%3A581%2C%22name%22%3A%22image.png%22%2C%22size%22%3A155829%2C%22display%22%3A%22inline%22%2C%22align%22%3A%22left%22%2C%22linkTarget%22%3A%22_blank%22%2C%22status%22%3A%22done%22%2C%22style%22%3A%22none%22%2C%22search%22%3A%22%22%2C%22margin%22%3A%7B%22top%22%3Atrue%2C%22bottom%22%3Atrue%7D%2C%22width%22%3A407%2C%22height%22%3A581%7D"></span></span></div><div style="text-align: left; background-color: #F5F5F5;"><span style="color: #000000; background-color: #F5F5F5;">感兴趣的可以戳这里看看完整的Demo代码:</span></div><div style="text-align: left; background-color: #F5F5F5;"><span style="color: #000000; background-color: #F5F5F5;"><span data-card-type="inline" data-ready-card="image" data-card-value="data:%7B%22src%22%3A%22https%3A%2F%2Fucc.alicdn.com%2Fpic%2Fdeveloper-ecology%2F8385fd322c2e4dd79e7ec862fb5ea39f.png%22%2C%22originWidth%22%3A401%2C%22originHeight%22%3A329%2C%22name%22%3A%22image.png%22%2C%22size%22%3A24399%2C%22display%22%3A%22inline%22%2C%22align%22%3A%22left%22%2C%22linkTarget%22%3A%22_blank%22%2C%22status%22%3A%22done%22%2C%22style%22%3A%22none%22%2C%22search%22%3A%22%22%2C%22margin%22%3A%7B%22top%22%3Atrue%2C%22bottom%22%3Atrue%7D%2C%22width%22%3A401%2C%22height%22%3A329%7D"></span></span></div><div style="text-align: left; background-color: #F5F5F5;"><span><a href="https://codepen.io/Chokcoco/pen/yqKrPR?editors=1100" target="_blank">CodePen Demo -- 掘金登录效果纯CSS实现</a></span></div><div style="text-align: left; background-color: #F5F5F5;"><span class="lake-fontsize-9"> </span></div><h2 id="Wo6AZ" style="text-align: left; background-color: #F5F5F5;"><span class="lake-fontsize-18">兼容性</span></h2><div style="text-align: left; background-color: #F5F5F5;"><span>好了,例子举例的也差不多了,下面到了杀人诛心的兼容性时刻,按照惯例,这种属性大概率是一片红色,看看 CANIUSE,截图日期(2018/08/02),其实也还不算特别惨淡。</span></div><div style="text-align: left; background-color: #F5F5F5;"><span><span data-card-type="inline" data-ready-card="image" data-card-value="data:%7B%22src%22%3A%22https%3A%2F%2Fucc.alicdn.com%2Fpic%2Fdeveloper-ecology%2Fff6c0ebc993e48028780a3dabab9ce4e.png%22%2C%22originWidth%22%3A749%2C%22originHeight%22%3A316%2C%22name%22%3A%22image.png%22%2C%22size%22%3A59214%2C%22display%22%3A%22inline%22%2C%22align%22%3A%22left%22%2C%22linkTarget%22%3A%22_blank%22%2C%22status%22%3A%22done%22%2C%22style%22%3A%22none%22%2C%22search%22%3A%22%22%2C%22margin%22%3A%7B%22top%22%3Atrue%2C%22bottom%22%3Atrue%7D%2C%22width%22%3A749%2C%22height%22%3A316%7D"></span></span></div><h2 id="ng27L" style="text-align: left; background-color: #F5F5F5;"><span class="lake-fontsize-18">最后</span></h2><div style="text-align: left; background-color: #F5F5F5;"><span>感谢耐心读完。本文只是抛砖引玉,期待发掘 <code>focus-within</code> 更多有意义的用法。</span></div><div style="text-align: left; background-color: #F5F5F5;"><span>更多精彩 CSS 技术文章汇总在我的 <a href="https://github.com/chokcoco/iCSS" target="_blank">Github -- iCSS</a> ,持续更新,欢迎点个 star 订阅收藏。</span></div><div style="text-align: left; background-color: #F5F5F5;"><span>好了,本文到此结束,希望对你有帮助 :)</span></div><div style="text-align: left; background-color: #F5F5F5;"><span>如果还有什么疑问或者建议,可以多多交流,原创文章,文笔有限,才疏学浅,文中若有不正之处,万望告知。</span></div><div style="text-align: left; background-color: #F5F5F5;"> </div>