如何根据CSS中的另一个元素的状态选择一个元素? [英] How do I select an element based on the state of another element in the page with CSS?

查看:257
本文介绍了如何根据CSS中的另一个元素的状态选择一个元素?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有元素可以反映不同的状态,由用户(:hover :focus 等等)或由服务器操纵( data-status =finished disabled 等)。



我可以定位已更改状态的元素,但我似乎无法根据元素的状态找到一种方法来定位DOM中的其他元素



示例:

 < section& 
< div>元素1< / div>
< div data-status =finished>元素2< / div>
< div>元素3< / div>
< / section>
< section>
< div>元素4< / div>
< div class =blink>元素5< / div>
< div>元素4< / div>
< div>元素4< / div>
< div class =spin>元素4< / div>
...
< / section>

或者只是渲染具有适当样式服务器端的元素。



有一个CSS选择器,让我指定哪些元素应该根据目标元素的状态来选择



类似:

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

这样,我也可以定位与CSS不同的父元素的元素?

解决方案

规范问题的一般答案


我根据CSS中另一个元素的状态选择一个元素


是取决于三个条件: / p>


  1. 这些元素的状态是否可以使用简单选择器表示,

  2. 使用组合器形成单个复杂选择器,并在

  3. 之间表示这两个元素之间是否可以将要定位的元素作为结果复杂选择器。

当前选择器标准有一些有趣的和有时可能强大的功能,它的设计方式使其在区域#2(#3是直接后果)极其有限。这些有限可能性中的一些列举在其他答案中,例如。通过最基本的使用孩子和兄弟的组合器,。 com / questions / 28708741 / how-do-i-select-an-element-based-on-the-state-of-another-element-in-the-page-wit / 28708762#28708762 / a>。



另一方面,问题中给出的问题不能使用当前在Selectors中可用的解决方案来解决。大多数情况下归结为缺少父选择器和/或先前的同级选择器,这两者都可能看起来像是微不足道的特征,但是具有某些含义使得难以定义或实现良好。总之:


  1. 是的,这些元素的状态可以使用简单的选择器来表示: div <对于前者, [data-status〜= finished] .blink


  2. 第一个元素可以用部分> div [data-status〜= finished] ,两个主题元素可以用 section + section> .blink section + section> .spin 。问题是,不可能编写一个包含所有这些结构的复杂选择器,因为组合器是单向的,并且没有子组合器的父对象在第一个元素。


  3. 假设前两个问题的答案也是是, .blink .spin 可以成为其自己的复杂选择器的主题。


如果你已经被指向这个问题,您尝试解决的问题,如上所述,由于这些限制,无法用Selectors解决。



即将到来的标准拥有一些新功能,将极大地丰富选择器语法和潜在地打开它(和CSS)到一系列新的可能性,包括对示例问题的可能解决方案。所有这些事情将在以下部分中讨论,但首先我将解释每个条件的含义以及它与给定示例的关系:



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



选择器的定义特性是它表示文档树中一个或多个元素的某个结构。这不仅仅是我编写的内容 - 您可以在内容丰富的概述中找到此说明的选择器标准


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



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


的一个或多个简单选择器。这个序列称为复合选择器(我使用Selectors 4的术语,因为它比选择器3中使用的更清晰 - 参见这个答案对于一个非穷尽的术语列表)。



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



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

  div [data-status〜= finished] 

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

  div [data-status〜= finished]:hover 

复合选择器通过组合器选择器。这些组合器,> + 符号您可能会熟悉,表示由每个复合选择器表示的元素之间的关系。有了这两个工具,你已经能够创建一些非常有趣的结果,如这里的其他答案所示。我将在这篇文章中更深入地介绍这些基础知识回答



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




  • 第一个元素是 div [data-status〜= finished] 。这是使用子组合程序>

      div [data-status〜= finished] 


  • 第二个 紧跟在第一个之后作为其兄弟。这是使用相邻同胞组合器 +

      code> 


  • 此外,第二个部分 code> .blink .spin 。这可以使用两个选择器来表示,每个孩子一个:

      section + section> .blink,
    section + section> .spin

    为什么需要两个选择器?在这种情况下,主要是因为目前没有语法将两个复合选择器细分为一个,因此您必须单独表示每个子元素。即将到来的Selectors 4标准引入了:matches()伪类,它将提供这种非常小的分组功能:

      section + section> :matches(.blink,.spin)




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





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



选择器的主题



选择器的主题始终由最右边的复合选择器表示。例如,选择器 section + section> div 表示三个元素,其中 div 是主题。您可能会说, div 已选择已定位



在CSS规则中,样式应用于由选择器主题表示的元素。任何子框和伪元素框在适当的情况下继承此元素的样式。 (例外是如果选择器的主体包括伪元素,在这种情况下,样式只能直接应用于伪元素。)



从上一节开始,我们有以下内容:




  • div [data-status〜= finished] is div [data-status〜= finished]

  • section + section 的主题是第二 选择器。

  • 节+节> .blink,section + section> .spin 分别是 .blink .spin

  • 使用:matches(),主题为 section + section& :matches(.blink,.spin):matches(.blink,.spin)



    • 因此,我们可能需要一个父选择器或前一个同级选择器。但请记住,选择器可以表示复杂的结构。



      这使我们从原来的问题到以下:


      有一个CSS选择器,让我指定哪些元素应该选择基于目标元素状态?


      这个问题的答案是no,并且将保持不变。但是,在选择器4的早期草稿(来自 FPWD 最多 2013年5月起的最新工作草案),有一项建议是



      一个潜在的解决方案

      一个新的功能,让你选择除最右边的复合选择器之外的任何复合选择器,并指定它作为选择器的主题。

      但是,最近删除了主题标识符,有利于:has()伪类href =http://api.jquery.com/has-selector =nofollow>通过jQuery )。我推测此处的可能原因:


      原因:has()更多用途是因为,使用主题选择器,它从来没有如果单个复杂选择器可以具有多个主体选择器(因为单个复杂选择器只能有一个主体)和/或如果功能伪类(例如:matches()接受主题选择器。但因为一个伪类是一个简单的选择器,你知道:has()可以被接受的任何一个伪类接受。


      所以当你不能改变选择器的主题时,:has()会完全注销这样做,由于其伪类的性质。最好的部分是,它这样做 - 然后一些 - 所有没有根本改变选择器语法。



      事实上,可以使用Selectors 4的:has()

        / *结合:matches()从上面的例子* / 
      section:has(> div [data-status〜= finished])+ section> div:matches(.blink,.spin)

      注意子组合器的使用:相对选择器参数为第一个的子节点。是的,这是世界上的Web开发人员一直想要的多年的难以捉摸的父选择器。



      由于:has()来自jQuery,您可以今天使用,虽然:matches()还不存在,必须替换为 .filter()



        $('section:has(> div [data-status〜= finished])+ section> div').filter('。blink,.spin').css('color','红色');  

       < script src = //ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js\"> ;</script><section> < div>元素1< / div> < div data-status =finished>元素2< / div> < div>元素3< / div>< / section>< section> < div>元素4< / div> < div class =blink>元素5< / div> < div>元素4< / div> < div>元素4< / div> < div class =spin>元素4< / div> ...< / section>  



      它还将允许您不仅目标元素不具有相同的父,而且还包括完全不相关的元素,包括在文档树中的位置可能彼此不同的元素。这将有效地消除上述条件#2,虽然这样做伴随着我将在一秒钟内得到的一个主要警告。例如,如果我们假设有问题的 div 元素可能出现在彼此之间没有结构关系的任何地方,:has()允许您执行:

       :root:has(div [data-status〜= finished])div:matches (.blink,.spin)

      ...找到 div.blink ,在文档树中的任何地方中存在 div [data-status〜= finished] 时,div.spin 元素必须是文档根元素的后代。



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



      这也是为什么:has()暂时从快速个人资料中排除,因此可能无法在CSS中使用,因为它在页面呈现期间需要实时选择器匹配,这是不可否认的性能关键的情况。它仍然可以通过DOM方法访问 querySelector() querySelectorAll() matches() (以及使用这些库的任何选择器库)。



      ,CSSWG有计划来测试有限的变体, code>:has()(例如,使用单个子组合器或同级组合器)来查看它们是否可以实现得足够好以包含在CSS中,这将满足 vast



      结论



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


      I have elements that can reflect different states, either triggered by the user (:hover, :focus, etc.) or manipulated by the server (data-status="finished", disabled, etc.).

      I can target the element that has a changed state, but I can't seem to find a way to target other elements in the DOM based on the state of the element in question.

      Example:

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

      or just render the elements with proper styles server-side.

      Is there a CSS selector that would let me specify which elements should get selected based on the target element's state?

      Something like:

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

      that would allow me to also target elements that don't have the same parent with CSS only?

      解决方案

      The general answer to the canonical question

      How do I select an element based on the state of another element in the page with CSS?

      is that it depends on exactly three conditions:

      1. whether the state of these elements can be represented using simple selectors,
      2. whether a structural relationship can be expressed between these two elements using combinators to form a single complex selector, and
      3. whether the element that you want to target can be made the subject of the resulting complex selector.

      While the current Selectors standard has some interesting and sometimes potentially powerful features, the way it is designed makes it extremely limited in area #2 (with #3 being a direct consequence). Some of these limited possibilities are enumerated in other answers, e.g. through the most basic use of child and sibling combinators, clever use of dynamic pseudo-classes (which actually relates to condition #1), or a combination of both.

      The problem given in the question, on the other hand, cannot be solved using what is currently available in Selectors for this reason. Most of this boils down to the lack of either a parent selector and/or a previous sibling selector, both of which may seem like trivial features, but have certain implications that make them difficult to define or implement well. In summary:

      1. Yes, the state of these elements can be represented using simple selectors: div and [data-status~=finished] for the former, and .blink and .spin for the latter two.

      2. The first element can be represented by section > div[data-status~=finished], and the two subject elements can be represented by section + section > .blink and section + section > .spin respectively. The problem is that it's not possible to write a complex selector incorporating all of these structures, because combinators are one-way, and there is no parent counterpart to the child combinator to join them at the first section element.

      3. Assuming the answer to the first two questions is also "yes", each of .blink and .spin can be made the subject of its own complex selector. (But more on that in the next section.)

      If you've been directed to this question, chances are the problem you're trying to solve, like the one given above, cannot be solved with Selectors due to these limitations.

      The upcoming standard boasts some new features that will greatly enrich selector syntax and potentially open it (and CSS) up to a host of new possibilities, including a possible solution to the example problem. All of these things will be covered in the following sections, but first I'll explain what each condition means and how it relates to the given example:

      Element states, and structural relationships between elements

      The defining characteristic of a selector is that it represents a certain structure of one or more elements in the document tree. This isn't just something I made up — you can actually find this description in the informative overview of the Selectors standard:

      A Selector represents a structure. This structure can be used as a condition (e.g. in a CSS rule) that determines which elements a selector matches in the document tree, or as a flat description of the HTML or XML fragment corresponding to that structure.

      Selectors may range from simple element names to rich contextual representations.

      Each element is represented by a sequence of one or more simple selectors. This sequence is known as a compound selector (I'm using terminology from Selectors 4 here as it is much clearer than what is used in Selectors 3 — see this answer for a non-exhaustive list of terms).

      Each simple selector represents a certain state of an element. There are simple selectors for matching the type (or tag name) of an element, a class name, an ID, or an arbitrary attribute. There are also pseudo-classes, which represent abstractions and other special states not directly represented within the document tree, such as the order and position of an element in its hierarchy (:nth-child(), :nth-of-type()), user interactions (:hover, :active, :focus, :checked), the visitedness of a hyperlink (:link, :visited), and much more.

      In the given example, the div element with a data-status attribute whose space-delimited value contains finished can be represented with a type selector and an attribute selector:

      div[data-status~=finished]
      

      If you want the selector to apply only when the pointer is over this element, simply throw in a :hover pseudo-class:

      div[data-status~=finished]:hover
      

      Compound selectors are linked via combinators to form complex selectors. These combinators, the >, + and ~ symbols that you may be familiar with, express a relationship between the elements represented by each compound selector. With these two tools alone, you're already able to create some very interesting results as shown in the other answers here. I explain these basics in even further depth in this answer.

      In the given example, the following structural relationships can be established:

      • The first section element is the parent of div[data-status~=finished]. This is represented using the child combinator >:

        section > div[data-status~=finished]
        

      • The second section immediately follows the first one as its sibling. This is represented using the adjacent sibling combinator +:

        section + section
        

      • Additionally, the second section is the parent of both .blink and .spin. This can be represented using two selectors, one for each child:

        section + section > .blink, 
        section + section > .spin
        

        Why are two selectors required? In this case it's mainly because there is currently no syntax for subgrouping two compound selectors into one, so you will have to represent each child element separately. The upcoming Selectors 4 standard introduces a :matches() pseudo-class that will provide this very subgrouping functionality:

        section + section > :matches(.blink, .spin)
        

      Now, since every compound selector in a complex selector represents one element, and thus section + section represents two elements that are siblings, section > div represents a parent and a child, and section + section > div represents a child of a next-sibling, you would think that a parent combinator and a previous-sibling combinator are quite redundant. So why do we commonly get these questions:

      And, more importantly, why is the answer to both of these questions no? The reason is addressed in the next point:

      Subject of a selector

      The subject of a selector is always represented by the rightmost compound selector. For example, the selector section + section > div represents three elements, of which div is the subject. You might say that the div is selected, or targeted, as in the question, but if you've ever wondered if there was a proper term, it's known as the subject of the selector.

      In a CSS rule, styles are applied to the element represented by the subject of the selector. Any child boxes and pseudo-element boxes inherit the styles from this element where appropriate. (The exception is if the subject of the selector includes a pseudo-element, in which case the styles are applied directly to the pseudo-element only.)

      Taking the selectors from the previous section, we have the following:

      • The subject of section > div[data-status~=finished] is div[data-status~=finished].
      • The subject of section + section is the second section selector.
      • The subjects of section + section > .blink, section + section > .spin are .blink and .spin respectively.
      • Using :matches(), the subject of section + section > :matches(.blink, .spin) is :matches(.blink, .spin).

      It might seem therefore that we do need a parent selector or a previous-sibling selector. But remember that selectors can already represent complex structures. Instead of simply adding new combinators that work opposite of existing ones, it makes sense to seek out a more flexible solution, and that is exactly what the CSSWG has been doing.

      Which brings us to the following from the original question:

      Is there a CSS selector that would let me specify which elements should get selected based on target element state?

      The answer to this is no, and will remain no. However, in the earlier drafts of Selectors 4 (from the FPWD up to the latest working draft from May 2013), there was a proposal for a new feature that would let you pick any of the compound selectors other than the rightmost one, and designate that as the subject of the selector.

      A potential solution

      However, the subject indicator was recently removed in favor of the :has() pseudo-class (that was in turn adopted from jQuery). I speculate on a likely reason here:

      The reason :has() is more versatile is because, with the subject selector, it was never made clear in any draft if a single complex selector could have more than one subject selector (since a single complex selector can only ever have one subject) and/or if functional pseudo-classes such as :matches() accepted the subject selector. But because a pseudo-class is a simple selector, you know that :has() can be accepted anywhere a pseudo-class is accepted.

      So while you cannot change the subject of a selector, :has() will completely write off the need to do so, due to its pseudo-class nature. And the best part is that it does this — and then some — all without fundamentally changing selector syntax.

      In fact, the example problem can be solved using Selectors 4's :has():

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

      Notice the use of a child combinator: this scopes the relative selector argument to just children of the first section. Yes, this is the elusive "parent selector" that Web developers the world over have been wanting for years.

      And since :has() comes from jQuery, you can use it today, although :matches() doesn't exist yet so you'll have to replace that with a call to .filter() in the meantime:

      $('section:has(> div[data-status~=finished]) + section > div')
          .filter('.blink, .spin')
          .css('color', 'red');

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

      It's so versatile, that it will also allow you to not only "target elements that don't have the same parent", but also elements that are completely unrelated, including elements whose positions in the document tree may vary irrespective of one another. This will effectively eliminate condition #2 above, although doing so comes with a major caveat which I'll get to in a second. For example, if we assume that the div elements in question may appear anywhere with no structural relation to one another, :has() allows you to do:

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

      ... which finds div.blink, div.spin when div[data-status~=finished] exists anywhere in the document tree, since any element in the document tree must be a descendant of the document root element.

      Now, the caveat that I mentioned, is that using arbitrary complex selectors with :has() can have serious performance implications, which is why for the longest time parent selectors were never implemented, and both the subject indicator and :has() have not been implemented yet. The latter two in particular are problematic because the "rightmost compound selector" definition serves as the basis of mainstream CSS selector engines, and these two features seek to challenge it altogether.

      This is also why :has() is tentatively excluded from the fast profile and may therefore not be usable in CSS, as it requires real-time selector matching during page rendering, a situation that is undeniably performance-critical. It will still be accessible through the DOM methods querySelector(), querySelectorAll() and matches() (and any selector libraries that happen to make use of them), however.

      That said, the CSSWG has plans to test limited variations of :has() (e.g. with a single child combinator or sibling combinator) to see if they can be implemented well enough to be included in CSS, which will fulfill the vast majority of use cases, including the first example above.

      Conclusion

      Unfortunately, CSS selector syntax remains extremely limited today; however, new proposals to the standard are set to bring powerful new possibilities, and a handful of these additions are based on features that selector libraries such as jQuery already offer. Here's hoping that implementations will support these new features for use in CSS.

      这篇关于如何根据CSS中的另一个元素的状态选择一个元素?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆