这篇文章发布于 2016年08月7日,星期日,16:26,归类于 CSS相关。 阅读 153969 次, 今日 14 次 34 条评论
by zhangxinxu from http://www.zhangxinxu.com/wordpress/?p=5559
本文全文转载需购买版权(500¥),摘要引流则免费,具体参见这里
一、为何CSS不支持父选择器?
这个问题的答案和“为何CSS相邻兄弟选择器只支持后面的元素,而不支持前面的兄弟元素?”是一样的。
浏览器解析HTML文档,是从前往后,由外及里的。所以,我们时常会看到页面先出现头部然后主体内容再出现的加载情况。
但是,如果CSS支持了父选择器,那就必须要页面所有子元素加载完毕才能渲染HTML文档,因为所谓“父选择器”,就是后代元素影响祖先元素,如果后代元素还没加载处理,如何影响祖先元素的样式?于是,网页渲染呈现速度就会大大减慢,浏览器会出现长时间的白板。加载多少HTML就可以渲染多少HTML,在网速不是很快的时候,就显得尤为的必要。比方说你现在看的这篇文章,只要文章内容加载出来就可以了,就算后面的广告脚本阻塞了后续HTML文档的加载,我们也是可以阅读和体验。但是,如果支持父选择器,则整个文档不能有阻塞,页面的可访问性则要大大降低。
有人可能会说,要不采取加载到哪里就渲染到哪里的策略?这样子问题更大,因为会出现加载到子元素的时候,父元素本来渲染的样式突然变成了另外一个样式的情况,体验非常不好。
“相邻选择器只能选择后面的元素”也是一样的道理,不可能说后面的HTML加载好了,还会影响前面HTML的样式。
所以,从这一点来讲,CSS支持“父选择器”或者“前兄弟选择器”的可能性要比其他炫酷的CSS特性要低,倒不是技术层面,而是CSS和HTML本身的渲染机制决定的。当然,以后的事情谁都说不准,说不定以后网速都是每秒几个G的,网页加载速度完全就忽略不计,说不定就会支持了。
二、如何在CSS中实现父选择器效果?
虽然说至今尚无浏览器支持原生的父选择器效果,但是,我们是可以通过其他手段来实现父选择器效果的,虽说不是100%完全,但是,一般的项目需求都是可以满足的,如何实现呢?如果用一句话解释原理就是“把兄弟元素作为祖先元素使用”。
本文介绍的“父选择器”技术,涉及多个技术tips,但是,幸运的是,这些特性从IE7浏览器开始都是支持的,因此,本文的模拟父选择器效果兼容IE7+浏览器。
类似这样的技术tips学习,你唾沫横飞说得天花乱坠也不如一个活蹦乱跳的实例效果好。
您狠狠地点击这里:子元素focus,容器边框高亮demo
下图为默认态效果:
类似这种效果大家一定见过,就是我们在写邮件的时候,在输入收件人的时候,会自动匹配名字,当我们选中此名字后,就会在输入区域的前面显示。实际在web网页开发的时候,这种交互也是会遇到的,比方说一次性给多个好友发送私信的时候,或者类似demo这里给《青云志》添加主演姓名信息。
一般而言,输入框藏在姓名列表的后面,就像这样:
长度不会很长,因为容易换到下一行。好了,现在需求来了,交互设计师希望用户focus这个小小输入框的时候,外面容器的灰色边框要高亮,就像下面这样:
请问如何实现?
根据我的妄加推断,市面上,90%及其以上都是使用JS实现的,类似这样:
$('input').focus(function() { $(this).parent().addClass('highlight'); });
因为没办法呀,没有浏览器支持父选择器!
此时,广大CSSer的心中一定在呐喊,如果天降一个父选择器该多好啊!这就是为何父选择器呼声很高的原因之一,因为确实有广泛的应用场景。
好了,回到现实,浏览器支持父选择器遥遥无期!
是固执地希望用纯CSS实现还是妥协JS实现?OK,告诉大家,纯CSS是可以实现的,而且就是一些简单的CSS特性组合而成实现的,没有什么高深难懂的地方,而且兼容性出奇的好,IE7+浏览器都支持。
例如,下图这个效果就是纯CSS实现的,通用DOM结构设计和其他选择器实现的:
那具体原理是怎样的呢?
大致分为下面3步:
- 原先的父级容器只处理结构,不负责外观。CSS代码如下:
.container { min-height: 120px; position: relative; z-index: 1; }
其中,这里的
z-index:1
用来创建新的层叠上下文,这样子元素的z-index:-1
不会超出容器,具体可参见“深入理解CSS中的层叠上下文和层叠顺序”一文。 - 新建一个元素负责容器的外观,放置在输入框的后面,使用absolute拉伸和容器尺寸一致。于是HTML结构变成这样:
<div class="container"> <input id="input" class="input"> <!-- 下面的label就是新建的负责容器外观的元素 --> <label class="border" for="input"></label> </div>
CSS代码如下:
.border { /* 尺寸自适应容器大小,假装是容器 */ position: absolute; left: 0; right: 0; top: 0; bottom: 0; /* 在输入框的下面 */ z-index: -1; /* 外观模拟 */ border: 1px solid #bbb; }
绝对定位元素,如果没有具体的
width
/height
限定,left/right以及top/bottom对立方位的数值会拉伸元素的尺寸。这里这里,全部都设为0
,和.container
所在元素组合,就形成了类似宽高100%同时box-sizing:border-box;
的效果,所以,如果你想让IE7浏览器实现类似border-box
的盒尺寸计算,某些情况下,就可以使用绝对定位拉伸,别说我没告诉你哦! - 通过相邻兄弟选择器,控制容器的样式变化,CSS代码如下:
.input:focus + .border { /* 容器边框颜色高亮 */ border-color: #1271E0; }
于是乎,当我们focus输入框的时候,那个大大的灰色边框就高亮了!看上去高亮的是外部的容器,实际上是输入框的兄弟元素,这就是上面所说的“把兄弟元素作为祖先元素使用”!
小结
根据我的实际开发经验,基本上,所以需要使用父选择器控制样式的地方,都可以使用这种策略实现。本文的例子就全当是抛砖引玉,当在座的诸位以后遇到类似需求的时候,希望可以想起这篇文章,说不定会对你的实现带来全新的思路。
更新于2022年7月21日
现在,在现代浏览器下,可以使用 :focus-within 伪类更方便的实现类似父选择器的效果,详见这篇文章:“详细了解CSS :focus-within伪类及其交互应用”。
类似的选择器还有 :target-within
伪类,不过兼容性还不是很好。
三、关于父选择器的一些八卦知识
此处八卦知识采撷自知乎“CSS 中能否选取父元素?”这个问题一丝的回答。
总结一下就是2个槽点:
-
CSS4中父选择器的语法变身了3次:
- 2011年,语法是
$E > F
,例如:$input:focus > .container {}
这种语法是网络上流传最多的用法,实际上,早就淘汰很久了,大家要明鉴啊!
- 2012-2013年的语法是
!E > F
,例如:!input:focus > .container {}
昙花一现。
- 2014年,语法使用:has()伪类(Relational Pseudo-class),例如:
.container:has(> input:focus) {}
- 2011年,语法是
-
最后一次的变身
:has
推迟到CSS5了。
恩,大家这下都明白了吧!
哈?明白什么?这还要问吗,那就是至少10年之内不会出现CSS父选择器了呀!还CSS5呢,也不看看CSS4这会儿才刚刚开始长毛呢!
所以大家还是醒醒,学学本文提供的实现思路吧!
本文为原创文章,尊重辛勤劳动,可以免费摘要、推荐或聚合,但完整转载需付费购买版权,详见转载协议声明
本文地址:http://www.zhangxinxu.com/wordpress/?p=5559
(本篇完)
- 详细了解CSS :focus-within伪类及其交互应用 (1.000)
- 来了,来了,CSS :has()伪类她来了 (0.582)
- CSS3 box-shadow兼容loading效果兼IE10+ CSS Hack介绍 (0.418)
- 折腾:2颗星星+纯CSS实现星星评分交互效果 (0.418)
- 从height:100%不支持聊聊CSS中的“死循环” (0.418)
- 小tip:CSS计数器+伪类实现数值动态计算与呈现 (0.165)
- CSS :visited伪类选择器隐秘往事回忆录 (0.165)
- 周知:CSS -webkit-伪元素选择器不再导致整行无效 (0.165)
- 伪类匹配列表数目实现微信群头像CSS布局的技巧 (0.165)
- 巧用:is()或:where()伪类让scoped的style依然全局匹配 (0.165)
- 使用::part伪元素改变Shadow DOM的CSS样式 (RANDOM - 0.045)
:has() chrome105 已经支持了
我管你叫兄弟你管我叫爸爸
hhhh
当我没说,没注意最后一行。
现在好像可以用:has来解决这个问题。
还行啊
受教了!
试试这个选择器 :focus-within,对表单有用
最近在想,是否可以借助vue的力量来实现css父元素和同级元素的选择器功能,在css中写了类似 .child < .parent{xxx} 的样式之后,在打包的时候,loader将css中的这种选择器样式编译成 .parent[v-child-parent]{xxx},然后额外生成一份规则,当vue在渲染更新dom结构的时候,去判断dom节点是否能够命中 .child < .parent 这个规则,也就是.parent元素下是否有.child子元素,命中的话,就给.parent这个元素加上v-child-parent属性。
同级元素的规则类似。
大神。我们现在有很多form表单,里面有许多输入框,用了jquery-validation校验插件。该插件如果一个输入框不能为空时,只需要添加class=”required”即可。保存form时如果没输入东西,会自动添加一个错误信息“不能为空”。
校验前代码是
姓名:
*
校验后是
姓名:
*
必填信息
我们想在必填的输入框后面加一个红色星号,现在的做法是凡是要加星号的地方写上*。这个有点烦。还有一种办法是用js处理,对所有input.required的父元素,append一个*。但是一个输入框有时需要required有时又不需要,比如级联显示,display:none有应该去掉required样式,一旦变成display:block又要加回required,还得js代码再处理,也很麻烦。
有没有一个简单的css办法,只要input有required就自动显示个红色星号。
类似于
.container:has(>input.required)::after{content:’*’,color:red}
这样的功能?
为何不使用
+
或~
这类兄弟选择器?厉害了我的哥
思路是这样,但没从根本解决问题,仅仅是通过障眼法通过前端思想去看待这个问题,如果说线上项目是后台输出的这么一段内容,你还是无法通过这个方式,这仅仅只能是作为项目开始的时候去考虑这个思路
原来是模拟啊,感觉被标题骗了…..
有这个结构,如何实现input:disabled后,“选择”文字样式的控制? 不能改变html结构,就纯css:
label
input type=”checkbox”
选择
label
你好,我这里的结构应该是input在label的外面~
首先感谢你的答复。
我这个是项目里多处用到的结构,也就是说既定结构不能改变为大前提,看能不能在css上想办法,修饰当input:disabled(人为添加/js添加)时,之后的文字的样式。
(label)
(input type=”checkbox” /)
选择
(/label)
看大神能不能够有什么高超的解决办法。
如果input默认是隐藏的话是可以的:
http://output.jsbin.com/lizesiriga
感谢!
果然是大神,思路就是广。虽然项目中不能隐藏input,但你提供的方式的思路,我绞尽脑汁也没想出来。
不太明白,为什么 .border的宽高都跟父盒子一致了,foucs的时候那个输入光标小竖条条怎么那么小?一脸懵逼~~
定位的4个角都是0
不是一个标签
所以,未来十年应该也不会有父级选择器。
所以说目前还是没有真正用 css 实现父选择器的方法。
哈哈也是一种思路,厉害了
学习了,真的是不错的思路,这个input和label的组合处理已经看到很多不错的用法了
前段时间实现了一个表格两行一起高亮的效果
也就是类似于选择某一个元素的前一个元素的效果
原理类似
十年~世界都变了好几遭了,CSS的发展真是够迟钝了
学习了
醍醐灌顶吖 ~赞
一不小心笑出了声!
看了好久你的博客了!好多知识都是需要长久积淀的~ 谢谢啦 无私的分享~~
很简单的知识,很巧妙的运用,大神就是大神,这就是和凡人的区别
前两天我利用css给单选框换装,跟您的方法大致相似。利用input的checked伪类,改变紧跟label的背景图片。但伪类兼容性是一个大问题……
记得有一位专家说过: 不会有 css4.