Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

神奇的选择器 :focus-within #36

Open
chokcoco opened this issue Jun 17, 2018 · 9 comments
Open

神奇的选择器 :focus-within #36

chokcoco opened this issue Jun 17, 2018 · 9 comments

Comments

@chokcoco
Copy link
Owner

chokcoco commented Jun 17, 2018

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;
}

就是这样:

focuswithinmaopao

CodePen Demo -- :focus-within 冒泡触发

这个选择器的存在,让 CSS 有了进一步的让元素持久停留在一种新状态的的能力。

下面几个例子,看看 :focus-within 可以提供什么能力,做些什么事情。

感应用户聚焦区域

它或它的后代获得焦点,这一点使得让感知获焦区域变得更大,所以,最常规的用法就是使用 :focus-within 感应用户操作聚焦区域,高亮提醒。

下面的效果没有任何 JS 代码:

cssfocuswithinpesudo

这里是什么意思呢?: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

运用上面思想,我们可以把效果做的更炫一点点,在某些场景制作一些增强用户体验的效果:

purecssfocus

DEMO -- PURE CSS FOCUS By :focus-within

TAB导航切换

在之前的一篇文章里,介绍了两种纯 CSS 实现的 TAB 导航栏切换方法:

纯CSS的导航栏Tab切换方案

现在又多了一种方式,利用了 :focus-within 可以在父节点获取元素获得焦点的特性,实现的TAB导航切换:

focuswithintab

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 <input> or <textarea> element that is currently displaying placeholder text.

另外,划重点,这个伪类是仍处于实验室的方案。也就是未纳入标准,当然我们的目的是探寻有意思的 CSS 。

意思大概就是,当 input 类型标签使用了 placeholder 属性有了默认占位的文字,会触发此伪类样式。配合:not()伪类,可以再改变当默认文字消失后的样式,再配合本文的主角,我们可以实现表单的一系列效果。

CSS 代码大概呈现成这样:

.g-container {
    width: 500px;
    height: 60px;

    input {
        height: 100%;
        width: 100%;

        &:not(:placeholder-shown) {
            ...
        }

        &:placeholder-shown {
            ...
        }
    }

    &:focus-within {
        ...
    }
}

实际效果如下:

placeholder

可以看到,上面的效果没有用到任何 JS,可以实现:

  1. 整个 input(包括父元素所在区域)获焦与非获焦样式控制
  2. placeholder 属性设置的文字出现与消失后样式控制

CodePen Demo -- :placeholder-shown && :focus-within

实现离屏导航

这个是其他很多文章都有提到过的一个功能,利用 focus-within 便捷的实现离屏导航,可以说将这个属性的功能发挥的淋漓尽致,这里我直接贴一个 codepen 上 Dannie Vinther 对这个效果的实现方案:

offscreennav

CodePen Demo -- Off-screen nav with :focus-within [PURE CSS]

实现掘金登录动效切换

juejin.im是我很喜欢的一个博客网站,它的登录有一个小彩蛋,最上面的熊猫在你输入帐号密码的时候会有不同的状态,效果如下:

juejin

利用本文所讲的 focus-within ,可以不借助任何 Javascript,实现这个动效:

juejinfocuswithin

感兴趣的可以戳这里看看完整的Demo代码:

CodePen Demo -- 掘金登录效果纯CSS实现

兼容性

好了,例子举例的也差不多了,下面到了杀人诛心的兼容性时刻,按照惯例,这种属性大概率是一片红色,看看 CANIUSE,截图日期(2018/08/02),其实也还不算特别惨淡。

image

最后

感谢耐心读完。本文只是抛砖引玉,期待发掘 focus-within 更多有意义的用法。

更多精彩 CSS 技术文章汇总在我的 Github -- iCSS ,持续更新,欢迎点个 star 订阅收藏。

好了,本文到此结束,希望对你有帮助 :)

如果还有什么疑问或者建议,可以多多交流,原创文章,文笔有限,才疏学浅,文中若有不正之处,万望告知。


最后,新开通的公众号求关注,形式希望是更短的篇幅,质量更高一些的技巧类文章,包括但不局限于 CSS:

image

@chokcoco chokcoco changed the title 神奇的选择器 :focus-within 神奇的选择器 :focus-within Aug 2, 2018
@fengcms
Copy link

fengcms commented Aug 2, 2018

还可以,主流基本都支持。不过国产移动端浏览器估计要等一等

@Yunkou
Copy link

Yunkou commented Aug 17, 2018

@fengcms 本来是锦上添花的。无妨无妨

@grandsong
Copy link

这个选择器太有用了!幸好我看了你的文章,不然就孤陋寡闻了。

@dengnan123
Copy link

学习了

@ghost
Copy link

ghost commented Jan 17, 2019

从效果上来理解的话,是不是可以理解为类父类选择器?

@chokcoco
Copy link
Owner Author

chokcoco commented Jan 17, 2019

@liuliangsir
我的理解是特殊情况下的父类选择器。(这个特殊情况指的是页面可以开始交互了)

其实 CSS 在这方面很谨慎,有子类选择器,但是没有父选择器。
因为,如果出现了父类选择器这种东西,整个页面的渲染速度将大打折扣,这个时候只有将整个文档的所有子元素加载完了,才能确定整个页面的样式。
如果没有父类选择器则可以放心的自顶向下渲染,因为子元素不会影响父元素的绘制。

所以这里出现了父类选择器也是因为,这种伪类状态父选择器并不会影响页面绘制的速度。

@grandsong
Copy link

本质上,就是对document.activeElement遍历其所有上级元素。
由于一个页面(其实是 document)任意时刻都只有一个document.activeElement,所以这个功能的性能成本很低。

@mu-yu
Copy link

mu-yu commented Jan 18, 2019

对于这样的结构 .parent > (.child1 + .child2),如果 .parent 要根据两个 .childfocus-within 状态应用不同的样式,纯 css 应该没法实现的把?…

@grandsong
Copy link

我看不能。应该必须使用JS。
在如今,React和Vue非常强大和易用的基础上,我们也没必要太钻研CSS的偏门(有的简直算是邪道)用法。

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

No branches or pull requests

6 participants