如何让CSS :nth-of-type选择器在span元素后重置计数器?
嘿,这个问题我之前也碰到过——CSS的:nth-of-type坑就坑在它是基于父元素下的同类型元素全局计数的,不管中间插了多少其他类型的元素(比如你的月份<span>),它的计数器都不会重置,所以跨月份的事件布局就乱套了。我给你两个解决方案,优先纯CSS,实在不行再上JS:
纯CSS解决方案(推荐,现代浏览器支持)
CSS Selectors Level 4新增的:nth-child(an+b of <selector>)语法刚好能解决这个问题,它允许我们只针对匹配特定选择器的元素来计数,而不是父元素下的所有同类型元素。
把你原来的CSS替换成下面这段:
.event-list .event { float: left; width: 30%; margin-left: 5%; background: #eee; margin-bottom: 1rem; /* 加个底部间距,视觉上更舒服 */ } span.month { width: 100%; border-bottom: solid 1px black; display: block; clear: both; margin: 1.5rem 0; /* 增加上下间距,区分不同月份 */ } /* 每个月份组内的第1、4、7...个事件(也就是每行的第一个) */ span.month ~ .event:nth-child(3n + 1 of span.month ~ .event) { margin-left: 0; clear: both; }
原理说明
span.month ~ .event:nth-child(3n + 1 of span.month ~ .event) 这句话的意思是:
所有跟在
<span class="month">后面的.event元素中,在它们自己的匹配集合里是第3n+1个的元素(也就是每个月份事件组里的第1、4、7...个)
这样每个月份后的事件都会重新开始计数,完全不受前面月份事件数量的影响,布局自然就正常了。
注:这个语法目前主流浏览器(Chrome、Firefox、Edge等)都支持,如果你需要兼容IE这类旧浏览器,就看下面的JS方案。
JS/JQuery解决方案(兼容旧浏览器)
如果纯CSS的方案不满足兼容性要求,我们可以用JS给每个月份后的事件组单独标记,再针对性写CSS。
JQuery代码
$(document).ready(function() { // 遍历每个月份标签 $('.event-list span.month').each(function() { // 获取当前月份后面的所有事件,直到下一个月份标签为止 var currentEvents = $(this).nextUntil('span.month', '.event'); // 给这些事件添加独有的组类 var groupNumber = $(this).index('.event-list span.month') + 1; currentEvents.addClass('month-group-' + groupNumber); // 给每个组内的第1、4、7...个事件添加“行首”类 currentEvents.filter(':nth-child(3n+1)').addClass('event-row-start'); }); });
对应的CSS
.event-list .event { float: left; width: 30%; margin-left: 5%; background: #eee; margin-bottom: 1rem; } span.month { width: 100%; border-bottom: solid 1px black; display: block; clear: both; margin: 1.5rem 0; } /* 行首事件的样式:左间距清零,强制换行 */ .event-row-start { margin-left: 0; clear: both; }
原理说明
我们通过JQuery把每个月份后的事件分成独立的组,然后给每个组内的行首事件添加专属类,这样CSS就能精准控制每个组的布局,完全避开全局计数的问题。
为什么你之前用波浪线~没成功?
~是通用兄弟选择器,它只能帮你选中某个元素后面的所有匹配元素,但:nth-of-type还是会基于父元素下的**所有同类型元素(也就是所有<div>)**来全局计数。比如January后的4个事件是父元素里的第1-4个<div>,February后的是第5-10个,第5个<div>是3n+2(3×1+2=5),所以会保留margin-left:5%,但它本该是新行的第一个,这就导致了布局错位。而上面的两种方案都能让计数器在每个月份后重置,完美解决问题。
内容的提问来源于stack exchange,提问作者Wilf Swann

