如何根据CSS中页面中另一个元素的状态选择元素?

eas*_*wee 20 css css-selectors css3

我有能够反映不同的状态的元件,或者由用户(触发:hover,:focus等)或由服务器操作(data-status="finished",disabled等).

我可以定位具有已更改状态的元素,但我似乎找不到基于所讨论元素的状态来定位DOM中的其他元素的方法.

例:

<section>
    <div>Element 1</div>
    <div data-status="finished">Element 2</div>
    <div>Element 3</div>
</section>
<section>
    <div>Element 4</div>
    <div class="blink">Element 5</div>
    <div>Element 4</div>
    <div>Element 4</div>
    <div class="spin">Element 4</div>
    ...
</section>
Run Code Online (Sandbox Code Playgroud)

或者只是使用服务器端的正确样式呈现元素.

是否有一个CSS选择器可以让我根据目标元素的状态指定应该选择哪些元素?

就像是:

div[data-status~=finished]:affect(.blink, .spin)
Run Code Online (Sandbox Code Playgroud)

这将允许我也只针对 CSS 没有相同父级的元素

Bol*_*ock 29

对规范问题的一般答案

如何根据CSS中页面中另一个元素的状态选择元素?

这取决于三个条件:

  1. 是否可以使用简单的选择器表示这些元素的状态,
  2. 是否可以使用组合在这两个元素之间表达结构关系以形成单个复合选择器,以及
  3. 是否可以将要定位的元素作为生成的复杂选择器的主题.

虽然目前的选择器标准具有一些有趣且有时可能具有强大功能的特性,但它的设计方式使其在区域#2中非常有限(#3是直接后果).这些有限的可能性中的一些在其他答案中列举,例如通过儿童和兄弟组合器的最基本使用,巧妙地使用动态伪类(其实际上与条件#1相关),或两者的组合.

另一方面,问题中给出的问题无法通过选择器中当前可用的原因解决.大多数情况归结为缺少父选择器和/或先前的兄弟选择器,这两者都可能看起来像琐碎的特征,但具有某些影响,使得它们难以定义或实现良好.综上所述:

  1. :是,这些元素的状态可以通过简单的选择器来表示div,并[data-status~=finished]为前,且.blink.spin后两者.

  2. 第一个元素可以用section > div[data-status~=finished],并且两个主题元素可以分别用section + section > .blink和表示section + section > .spin.问题在于,不可能编写包含所有这些结构的复杂选择器,因为组合器是单向的,并且子组合器没有父对应物在第一个section元素处连接它们.

  3. 假设回答前两个问题也是"是",每个的.blink.spin可以做了它的主题本身复杂的选择.(但在下一节中有更多内容.)

如果您已经被引导到这个问题,那么您尝试解决的问题很可能就像上面给出的那样,由于这些限制而无法通过选择器解决.

即将到来的标准拥有一些新的功能,这将大大丰富选择语法和潜在打开它(CSS)达到新的可能性,其中包括一个可能的解决方案,以例题的主机.所有这些内容将在以下部分中介绍,但首先我将解释每个条件的含义以及它与给定示例的关系:

元素状态和元素之间的结构关系

选择器的定义特征是它表示文档树中一个或多个元素的特定结构.这不仅仅是我编写的内容 - 您实际上可以在Selectors标准信息概述中找到此描述:

选择器表示结构.该结构可以用作确定选择器在文档树中匹配哪些元素的条件(例如,在CSS规则中),或者用作对应于该结构的HTML或XML片段的平面描述.

选择器的范围可以从简单的元素名称到丰富的上下文表示.

每个元素由一个或多个简单选择器的序列表示.这个序列被称为复合选择器(我在这里使用了选择器4中的术语,因为它比选择器3中使用的更清晰 - 请参阅此答案以获得非详尽的术语列表).

每个简单选择器代表元素的某个状态.有简单的选择器用于匹配元素的类型(或标记名称),类名,ID或任意属性.也有伪类,其表示抽象等特殊状态不直接文档树中表示的,例如在它的层次结构中的顺序和元件的位置(:nth-child(),:nth-of-type()),用户交互(:hover,:active,:focus,:checked),a的visitedness超链接(:link,:visited)等等.

在给定的示例中,div具有data-status空格分隔值包含的属性的元素finished可以使用类型选择器和属性选择器表示:

div[data-status~=finished]
Run Code Online (Sandbox Code Playgroud)

如果希望选择器仅在指针位于此元素上时应用,则只需引入:hover伪类:

div[data-status~=finished]:hover
Run Code Online (Sandbox Code Playgroud)

化合物选择剂通过组合子连接形成复合选择子.这些组合子的>,+~符号,你可能熟悉,表达由每种化合物选择所表示的元素之间的关系.仅使用这两个工具,您就可以创建一些非常有趣的结果,如此处的其他答案所示.我在这个答案中进一步深入解释了这些基础知识.

在给定的示例中,可以建立以下结构关系:

现在,由于复杂选择器中的每个复合选择器都代表一个元素,因此section + section表示两个兄弟元素,section > div代表父元素和子元素,并section + section > div表示下一个兄弟的子元素,您会认为父组合子和前一个元素-sibling组合器是非常多余的.那么为什么我们常常会遇到这些问题:

而且,更重要的是,为什么这两个问题的答案都没有?原因在下一点解决:

选择者的主题

选择器的主题始终由最右边的复合选择器表示.例如,选择器section + section > div表示三个元素,其中div是主题.你可能会说问题是在问题中div选中或被定位的,但是如果你曾经想过是否有一个合适的术语,那么它就被称为选择者的主题.

在CSS规则中,样式应用于由选择器主题表示的元素.任何子框和伪元素框都会在适当的位置继承此元素的样式.(例外情况是选择器的主题包含伪元素,在这种情况下,样式仅直接应用于伪元素.)

采用上一节中的选择器,我们有以下内容:

  • 主题section > div[data-status~=finished]div[data-status~=finished].
  • 主题section + section第二个 section选择器.
  • 受试者分别section + section > .blink, section + section > .spin.blink.spin.
  • 使用:matches(),主题section + section > :matches(.blink, .spin):matches(.blink, .spin).

因此,我们似乎确实需要父选择器或前一个兄弟选择器.但请记住,选择器已经可以代表复杂的结构.而不是简单地添加与现有组合相反的新组合器,寻找更灵活的解决方案是有意义的,这正是CSSWG一直在做的事情.

从原始问题中我们得到以下内容:

是否有一个CSS选择器可以让我指定哪些元素应该根据目标元素状态进行选择?

对此的答案是否定的,并且仍然是否定的.然而,在选择器4的早期草案中(从FPWD2013年5月的最新工作草案),有一个新功能的建议,可以让你选择除最右边的之外的任何复合选择器,并指定作为选择者的主题.

潜在的解决方案

然而,最近删除了主题指示符以支持:has()伪类(反过来从jQuery采用).我在这里猜测一个可能的原因:

原因:has()更通用是因为,对于主题选择器,如果单个复杂选择器可以具有多个主题选择器(因为单个复杂选择器只能有一个主题)和/或功能伪类如:matches()接受主题选择器.但是因为伪类是一个简单的选择器,你知道:has()在接受伪类的任何地方都可以接受.

因此,虽然您无法更改选择器的主题:has(),但由于其伪类性质,将完全注销需要这样做.最好的部分是它做到了 - 然后是一些 - 所有这些都没有从根本上改变选择器语法.

实际上,使用Selectors 4 可以解决示例问题:has():

/* Combined with the :matches() example from above */
section:has(> div[data-status~=finished]) + section > div:matches(.blink, .spin)
Run Code Online (Sandbox Code Playgroud)

请注意子组合子的使用:这将相对选择器参数范围仅限于第一个子组合section.是的,这是世界各地的Web开发人员多年来一直想要的难以捉摸的"父选择器".

而且由于:has()它来自jQuery,你现在可以使用它,虽然现在:matches()还不存在,所以你必须.filter()在此期间通过调用替换它:

$('section:has(> div[data-status~=finished]) + section > div')
    .filter('.blink, .spin')
    .css('color', 'red');
Run Code Online (Sandbox Code Playgroud)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<section>
    <div>Element 1</div>
    <div data-status="finished">Element 2</div>
    <div>Element 3</div>
</section>
<section>
    <div>Element 4</div>
    <div class="blink">Element 5</div>
    <div>Element 4</div>
    <div>Element 4</div>
    <div class="spin">Element 4</div>
    ...
</section>
Run Code Online (Sandbox Code Playgroud)

它是如此多才多艺,它还允许您不仅"目标不具有相同父元素的元素",而且还允许您完全不相关的元素,包括文档树中的位置可能不同而彼此不同的元素.这将有效地消除上面的条件#2,虽然这样做会带来一个重要的警告,我将在一秒钟内得到.例如,如果我们假设所讨论的div元素可能出现在彼此之间没有结构关系的任何地方,则:has()允许您执行以下操作:

:root:has(div[data-status~=finished]) div:matches(.blink, .spin)
Run Code Online (Sandbox Code Playgroud)

... 在文档树中的任何位置存在div.blink, div.spin时查找,因为文档树中的任何元素必须是文档根元素的后代.div[data-status~=finished]

现在,我提到的警告是,使用任意复杂的选择器:has()会产生严重的性能影响,这就是为什么从未实现父选择器的最长时间,以及主题指示器和:has()尚未实现的原因.特别是后两者是有问题的,因为"最右边的复合选择器"定义是主流CSS选择器引擎的基础,并且这两个特征试图完全挑战它.

这也是:has()暂时从快速配置文件中排除的原因,因此可能无法在CSS中使用,因为它需要在页面呈现期间进行实时选择器匹配,这种情况无疑是性能关键的.它仍然会通过DOM方法访问querySelector(),querySelectorAll()并且matches()(和发生在使用它们的任何选择库),但是.

这就是说,CSSWG有计划,以测试的有限变化:has()(例如,使用一个子组合子或兄弟姐妹组合子),看看他们是否可以实现不够好,包括在CSS,这将满足广大大多数的使用案例,其中包括了以上第一个例子.

结论

不幸的是,CSS选择器语法今天仍然非常有限 然而,标准的新提议被设置为带来强大的新可能性,并且少数这些添加基于诸如jQuery之类的选择器库已经提供的特征.这里希望实现将支持这些用于CSS的新功能.

  • 我不认为这个答案足够长的Bolty. (7认同)

eas*_*wee 17

您对CSS的当前状态可以实现的目标非常有限.

简而言之 - 您可以使CSS元素对元素的状态更改做出反应,如果它们具有相同的父AND并且是兄弟元素,或者父元素的子元素.


CSS中元素的状态由伪类处理,伪类覆盖了浏览器根据用户输入处理的大多数典型交互.

虽然这使您能够在DOM树中处理元素及其子元素的当前状态的视觉外观,但您仍然无法使其他非子元素对您当前的状态做出反应(通过样式的可视更改)因为CSS没有提供特定类型的选择器来以灵活的方式执行此操作.

但是,您可以将伪类与其他类型的CSS选择器结合使用,并在某些情况下使其工作(我将使用悬停状态,因为它是最明显的):

伪类+相邻的兄弟选择器

相邻的兄弟选择器匹配if element1element2在文档树中共享相同的父级并且element1紧接在之前element2.(相邻兄弟选择器的W3C规范)

div:hover + div {
    background:red;
}
Run Code Online (Sandbox Code Playgroud)
Hover on elements:
<div>Element 1</div>
<div>Element 2</div>
<div>Element 3</div>
<div>Element 4</div>
<div>Element 5</div>
Run Code Online (Sandbox Code Playgroud)

伪类+一般兄弟组合子

这种组合与相邻的兄弟选择器的工作方式相同,只是所选择的元素不需要立即接替第一个元素; 它可以出现在任何地方.

div:hover ~ div {
    background:red;
}
Run Code Online (Sandbox Code Playgroud)
<div>Element 1</div>
<section>Element 2</section>
<em>Element 3</em>
<div>Element 4</div>
<div>Element 5</div>
Run Code Online (Sandbox Code Playgroud)

属性选择器+常规兄弟组合子/属性选择器+相邻的兄弟选择器

DOM元素的状态通常存储在数据属性中.CSS为您提供了一个属性选择器,它允许您根据属性的值应用样式.

以下示例为元素后面的所有元素设置较低的不透明度,状态为"finished":

div[data-status~=finished] ~ div {
    opacity: 0.3;
}
Run Code Online (Sandbox Code Playgroud)
<div>Element 1</div>
<div data-status="finished">Element 2</div>
<div>Element 3</div>
<div>Element 4</div>
<div>Element 5</div>
Run Code Online (Sandbox Code Playgroud)

:不()

在某些情况下,:not()可以帮助您选择所有其他没有激活状态的元素,但是CSS3中尚不支持非简单选择器:not(),尽管它们是在新选择器级别中提出的4稿.所以目前,你不能这样做div:not(.class, .class)- 因为只支持简单的选择器(类型选择器,通用选择器,属性选择器,类选择器,ID选择器或伪类).


最后,您可以构建一些可以实现您想要的功能的复杂选择器,但通常非常静态,并且可能会在DOM结构更改后立即停止工作,但对于极端情况,您可以解决问题.MDN的伪选择器列表在组合时非常方便.

不幸的是,在写这篇文章的那一刻,你仍然需要在CSS之外处理它以获得可靠的解决方案.

  • 〜不仅很棒,而且非常有用〜感谢您花时间写这篇文章!++ (2认同)
  • 仅供参考:`:not()`允许在CSS 3中使用属性和类型选择器,http://www.w3.org/TR/css3-selectors/#simple-selectors-dfn - 它只需要是一个"简单"选择器,因此您不能使用多个选择器的组合. (2认同)

Nae*_*ikh 6

由于@easwee已经发布了一个很好的答案,我不会重复他讨论的任何伪类选择器,


我们现在有一个新的伪类,在css3中是:target.

:targetCSS中的伪选择器匹配URL中的哈希值和元素的id相同.

(具体用途:target是在视口顶部定位并且可见的样式元素)

因此,当与其他伪类或兄弟选择器一起使用时,基于用户交互(具体点击)实际上可以(错误地)使用它来更改其他元素样式.

例如:父母的兄弟姐妹的目标孩子.

:target section div[data-status=finished] {
  color: red;
}
a,
a:visited {
  text-decoration: none;
  color: #000;
}
section {
  border: 1px solid grey;
}
Run Code Online (Sandbox Code Playgroud)
<nav id='nav'>
  <h4>This is Navigation section</h4>
  <section>
    sibling
    <div><a href='#nav'>(child)click me to target child  of sibling of parent</a>
    </div>
  </section>
  <section>
    sibling
    <div data-status="finished">(child)I am child of parent of sibling</div>
  </section>

</nav>
Run Code Online (Sandbox Code Playgroud)

请注意,在:target引入之前,无法在html层次结构中设置父元素或任何其他元素的样式.它仍然是新的,做这样的事情(根据另一个元素的点击选择另一个元素)并不是为什么:target设计的原因.

缺点:

  1. 使用目标定位元素,不需要在单个页面中使用太多,否则会使您无意中浏览整个页面.

  2. 元素的目标样式仍保留在其上,直到目标保持不变.

你可以玩这个小提琴