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

时间:2015-02-25 00:00:04

标签: css css3 css-selectors

我的元素可以反映不同的状态,可以由用户(: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>

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

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

类似的东西:

div[data-status~=finished]:affect(.blink, .spin)

这样我还可以定位那些与CSS不具有相同父级的元素吗?

3 个答案:

答案 0 :(得分:28)

规范问题的一般答案

  

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

这取决于三个条件:

  1. 是否可以使用简单的选择器表示这些元素的状态,
  2. 是否可以使用组合器在这两个元素之间表达结构关系以形成单一复杂选择器,并且
  3. 是否可以将要定位的元素作为生成的复杂选择器的主题。
  4. 虽然current Selectors standard具有一些有趣且有时可能具有强大功能的特征,但它的设计方式使其在区域#2中非常有限(#3是直接后果)。这些有限的可能性中的一些在其他答案中列举,例如,通过儿童和兄弟组合子的最基本用法clever use of dynamic pseudo-classes(实际上与条件#1相关)或a combination of both

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

    1. 是的,可以使用简单的选择器表示这些元素的状态:前者为div[data-status~=finished],后者为.blink.spin 2。

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

    3. 假设前两个问题的答案也是&#34;是&#34;,.blink.spin中的每一个都可以成为其拥有<的主题/ em>复杂选择器。 (但在下一节中有更多内容。)

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

      upcoming standard拥有一些新功能,这些功能将极大地丰富选择器语法,并可能打开它(和CSS),直到一系列新的可能性,包括示例问题的可能解决方案。所有这些内容将在以下部分中介绍,但首先我将解释每个条件的含义以及它与给定示例的关系:

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

      选择器的定义特征是它表示文档树中一个或多个元素的特定结构。这不仅仅是我编写的内容 - 您实际上可以在informative overview of the Selectors standard中找到此说明:

        

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

           

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

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

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

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

      div[data-status~=finished]
      

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

      div[data-status~=finished]:hover
      

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

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

      • 第一个section元素是div[data-status~=finished]的父元素。这是使用child combinator >

        表示的
        section > div[data-status~=finished]
        
      • 第二个section紧随第一个section + section 作为其兄弟。这是使用adjacent sibling combinator +

        表示的
        section
      • 此外,第二个.blink.spinsection + section > .blink, section + section > .spin 的父级。这可以使用两个选择器来表示,每个选择器一个:

        :matches()

        为什么需要两个选择器?在这种情况下,它主要是因为subgrouping two compound selectors into one目前没有语法,因此您必须分别表示每个子元素。即将推出的Selectors 4标准引入了一个section + section > :matches(.blink, .spin) 伪类,它将提供这种非常小的分组功能:

        section + section

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

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

      选择器的主题

      选择器的subject始终由最右边的复合选择器表示。例如,选择器div代表三个元素,其中div是主题。您可能会说section > div[data-status~=finished] 已选择已定位,如问题所示,但如果您曾经想过是否有合适的术语,它被称为选择者的主题。

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

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

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

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

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

        

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

      对此的答案是否定的,并且将保持不行。但是,在选择器4的早期草稿中(从FPWDthe latest working draft from May 2013),有一个新功能的建议可以让你选择除最右边的那个之外的任何复合选择器,并将其指定为选择者的主题。

      潜在的解决方案

      但是,最近删除了主题指标,转而支持:has()伪类(依次为adopted from jQuery)。我推测可能的原因是here

        

      :matches()更通用的原因是因为使用主题选择器,如果单个复杂选择器可以有多个主题选择器,则任何草稿都不会明确(因为单个复杂选择器只能使用有一个主题)和/或函数伪类如:has()是否接受了主题选择器。但是因为伪类是一个简单的选择器,你知道在接受伪类的任何地方都可以接受:has()

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

      事实上,使用选择器4 /* Combined with the :matches() example from above */ section:has(> div[data-status~=finished]) + section > div:matches(.blink, .spin) 可以解决示例问题

      section

      注意使用子组合子:这将相对选择器参数范围仅限于第一个:has()的子项。是的,这是一个难以捉摸的父母选择器&#34;全世界的Web开发人员多年来都一直在想。

      由于:matches()来自jQuery,你今天可以使用 ,虽然.filter()还不存在,所以你必须更换它在此期间打电话给$('section:has(> div[data-status~=finished]) + section > div') .filter('.blink, .spin') .css('color', 'red');

      &#13;
      &#13;
      <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>
      &#13;
      div
      &#13;
      &#13;
      &#13;

      它是如此多才多艺,它不仅可以让您不仅拥有不具有相同父母的元素,还可以使您完全无关的元素,包括元素其在文档树中的位置可以彼此不同地变化。这将有效地消除上面的条件#2,尽管这样做会带来一个重要的警告,我会在一秒钟之内完成。例如,如果我们假设有问题的:has()元素可能出现在彼此之间没有结构关系的任何地方,:root:has(div[data-status~=finished]) div:matches(.blink, .spin) 允许您这样做:

      div.blink, div.spin

      ...在文档树中的任何位置存在div[data-status~=finished] 时找到:has(),因为文档树中的任何元素都必须是文档根元素的后代。

      现在,我提到的警告是,使用:has()的任意复杂选择器会产生严重的性能影响,这就是为什么从未实现父选择器的最长时间,以及主题指示符和{{ 1}}尚未实施。特别是后两者是有问题的,因为最右边的化合物选择器&#34;定义serves as the basis of mainstream CSS selector engines,这两个特征试图完全挑战它。

      这也是:has()暂时从fast profile中排除的原因,因此可能无法在CSS中使用,因为它需要在页面呈现期间进行实时选择器匹配,这种情况无疑是性能 - 危急。但仍然可以通过DOM方法querySelector()querySelectorAll()matches()(以及碰巧使用它们的任何选择器库)访问它。

      也就是说,CSSWG有plans来测试:has()的有限变体(例如,使用单个子组合子或兄弟组合子),看看它们是否可以很好地实现以包含在CSS中,这将完成浩大的大多数用例,包括上面的第一个例子。

      结论

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

答案 1 :(得分:17)

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

简而言之 - 你可以让CSS元素对元素的状态变化作出反应,如果它们具有相同的父AND并且是兄弟元素,或者是父元素的子元素。


CSS中元素的状态由pseudo-classes处理,它涵盖了浏览器根据用户输入处理的大多数典型交互。

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

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

伪类+相邻的兄弟选择器

如果element1element2在文档树中共享同一个父级,并且element1紧接在element2之前,则相邻的兄弟选择器匹配。 (W3C specification of Adjacent sibling selectors

div:hover + div {
    background:red;
}
Hover on elements:
<div>Element 1</div>
<div>Element 2</div>
<div>Element 3</div>
<div>Element 4</div>
<div>Element 5</div>

伪类+一般兄弟组合

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

div:hover ~ div {
    background:red;
}
<div>Element 1</div>
<section>Element 2</section>
<em>Element 3</em>
<div>Element 4</div>
<div>Element 5</div>

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

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

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

div[data-status~=finished] ~ div {
    opacity: 0.3;
}
<div>Element 1</div>
<div data-status="finished">Element 2</div>
<div>Element 3</div>
<div>Element 4</div>
<div>Element 5</div>

<强>:不()

在某些情况下,:not()可以帮助您选择所有其他没有相关状态的元素,但是,CSS3中尚不支持非简单选择器:not(),尽管它们是在Selectors Level 4 draft新草案中提出。所以目前,你可以div:not(.class, .class) - 因为只支持简单的选择器(类型选择器,通用选择器,属性选择器,类选择器,ID选择器或伪类)。


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

不幸的是,在编写本文时,您仍然需要在CSS之外处理它以获得可靠的解决方案。

答案 2 :(得分:6)

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

<小时/> 我们现在在css3中使用的新伪类是:target

  

CSS中的:target伪选择器匹配时的哈希值   URL和元素的id是相同的。

(具体用途:目标是在视口顶部有针对性且可见的样式元素)

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

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

:target section div[data-status=finished] {
  color: red;
}
a,
a:visited {
  text-decoration: none;
  color: #000;
}
section {
  border: 1px solid grey;
}
<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>

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

缺点

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

  2. 目标元素保留在其上,直到目标保持不变。

  3. 您可以使用此 fiddle