CSS有父级选择器吗?

4116

如何选择作为锚点元素的直接父级的 <li> 元素?

例如,我的 CSS 如下:

li < a.active {
    property: value;
}

很明显可以用JavaScript做到这一点,但我希望存在CSS Level 2本身的解决方法。

我正在尝试样式化的菜单是由CMS生成的,所以我不能将活动元素移动到<li>元素中...(除非我主题化菜单创建模块,但我宁愿不这样做)。


9
今天发布的 Safari Tech Preview 137 首次引入了 :has() 选择器的实现。 - TylerH
希望有一种与JavaScript el.parenteElement.parenteElement... 一致的工作方式,比如 a:active:parent:parent 或者甚至是 a::active::parent::parent,用两个分号。这不仅更符合现有伪类的逻辑,而且更易于使用和链接,以便在需要时向上多级。我真的希望有这种实现,因为我真的很讨厌使用 :has(something):它既没有逻辑性,也没有直观性和人体工程学可用性。JavaScript 的方式更好,远胜于 :has()。 - willy wonka
截至今天,大多数现代浏览器都支持最新版本,例如:Chrome / Android 浏览器 / Chrome for Android 105+、Edge 105+、Safari / Safari iOS 15.4+ 和 Opera 91+。只有 Firefox 103 至 106 不支持默认功能,您需要启用它。对于移动设备:(Opera mini、Samsung internet 和 Firefox mobile 尚不支持该功能)。 - Abzoozy
1
@Abzoozy 感谢您的更新。为了使您的有用评论更加完善,您能否定义“它”,并说明哪个 Firefox 首选项启用此功能,直到它正式推出? - RockPaperLz- Mask it or Casket
2
现在在Chrome 105中 https://developer.chrome.com/blog/has-m105/ - northamerican
显示剩余6条评论
33个回答

11

CSS父级选择器(也称为:has()选择器)已经在Safari TP 137中最终实现。该功能目前也正在Chrome中实现。(MDN文档

通过伪类:has()进行父级选择。例如,div:has(> .child)将选择所有具有child类的子元素的<div>元素。

其他示例:

选择元素的直接父元素

<div>
  <p>Child Element</p>
</div>
div:has(> p)

选择一个元素的所有父级元素

<div id="grandparent">
      <div id="parent">
            <div id="child"></div>
      <div>
</div>
以下选择器将同时选择grandparentparent
div:has(.child)

您还可以将其用于嵌套选择器,甚至与其他伪类一起使用:

div:has(> :nth-child(10))

其他有效的CSS运算符可以用来自定义查询。

关注caniuse.com/css-has了解浏览器兼容性。


能否帮忙将 $('section:has(h1.heading1)').css('padding', 0); 转换成原生JavaScript代码? - CodeForGood
2
@CodeForGood document.querySelector('section:has(h1.heading1)').style.padding = 0,但它可能尚未在所有浏览器中起作用。 - Metabolic
你的代码抛出了 Uncaught DOMException: Document.querySelector: 'section:has(h1.heading1)' 不是一个有效的选择器 错误。 - CodeForGood

10

简短的回答是 不行;在CSS中,我们目前没有父选择器,但是如果您不必交换元素或类,第二个选项是使用JavaScript。类似这样:

var activeATag = Array.prototype.slice.call(document.querySelectorAll('a.active'));

activeATag.map(function(x) {
  if(x.parentNode.tagName === 'LI') {
    x.parentNode.style.color = 'red'; // Your property: value;
  }
});

如果您在应用程序中使用jQuery,也可以使用更短的方式:

或者如果你在你的应用程序中使用 jQuery,这是更短的方法:

$('a.active').parents('li').css('color', 'red'); // Your property: value;

或者 parentElement,类似的东西 - 只需使用 document.getElementById().parentElement 等。 - Andrew

10

从技术上讲,没有直接的方法来实现这一点。但是,您可以使用jQuery或JavaScript解决这个问题。

不过,您也可以像这样做。

a.active h1 {color: blue;}
a.active p {color: green;}

jQuery

$("a.active").parents('li').css("property", "value");

如果您想使用jQuery实现此操作,以下是jQuery parent选择器的参考文献。


你能否帮忙将 $('section:has(h1.heading1)').css('padding', 0); 转换成纯JavaScript代码? - CodeForGood

10
尽管标准CSS目前没有父选择器,但我正在开发一个名为axe(即增强型CSS选择器语法/ ACSSSS)的(个人)项目。它包括7个新选择器,其中包括:

  1. 一个直接父元素选择器<(与>相反)
  2. 一个任意祖先元素选择器^(与[SPACE]相反)

axe目前处于相对早期的BETA开发阶段。

在此处查看演示:

.parent {
  float: left;
  width: 180px;
  height: 180px;
  margin-right: 12px;
  background-color: rgb(191, 191, 191);
}

.child {
  width: 90px;
  height: 90px;
  margin: 45px;
  padding-top: 12px;
  font-family: sans-serif;
  text-align: center;
  font-size: 12px;
  background-color: rgb(255, 255, 0);
}

.child.using-axe < .parent {
  background-color: rgb(255, 0, 0);
}
<div class="parent">
  <div class="child"></div>
</div>

<div class="parent">
  <div class="child using-axe">Here, the axe parent selector turns the outer square red.</div>
</div>


<script src="https://rouninmedia.github.io/axe/axe.js"></script>

在上面的例子中,<直接父选择器,因此

  • .child.using-axe < .parent

表示:

.child.using-axe的任何直接父级,它是.parent

你也可以使用以下方式:

  • .child.using-axe < div

这将意味着:

.child.using-axe的任何直接父级,它是一个div


3
目前该项目处于早期测试阶段,您可以(至少目前)通过在HTML文档的末尾,在</body>之前包含<script src="https://rouninmedia.github.io/axe/axe.js"></script>,以便在CSS样式中启用辅助功能选择器。 - Rounin

10

W3C排除了这样一个选择器,因为它对浏览器的性能会产生巨大的影响。


30
因为 DOM 是一棵树形结构,所以在访问子节点之前必须先访问其父节点,所以简单地向上返回一个节点即可。false. - NullVoxPopuli
9
CSS选择器是一种队列,因此会评估选择器顺序而不是文档XPath或DOM层次结构。 - Paul Sweatte
4
至少这是他们告诉我们的。 - yunzen

4

有什么想法吗?

如果CSS4在“向后走”方面添加一些钩子,那就会变得很花哨。但在那之前,我们可以(虽然不建议)使用checkbox和/或radio input打破通常连接事物的方式,并且通过这个方法允许CSS在其正常范围之外进行操作...

/* Hide things that may be latter shown */
.menu__checkbox__selection,
.menu__checkbox__style,
.menu__hidden {
  display: none;
  visibility: hidden;
  opacity: 0;
  filter: alpha(opacity=0); /* Old Microsoft opacity */
}


/* Base style for content and style menu */
.main__content {
  background-color: lightgray;
  color: black;
}

.menu__hidden {
  background-color: black;
  color: lightgray;
  /* Make list look not so _listy_ */
  list-style: none;
  padding-left: 5px;
}

.menu__option {
  box-sizing: content-box;
  display: block;
  position: static;
  z-index: auto;
}

/* &#9660; - \u2630 - Three Bars */
/*
.menu__trigger__selection::before {
  content: '\2630';
  display: inline-block;
}
*/

/* &#9660; - Down Arrow */
.menu__trigger__selection::after {
  content: "\25BC";
  display: inline-block;
  transform: rotate(90deg);
}


/* Customize to look more `select` like if you like */
.menu__trigger__style:hover,
.menu__trigger__style:active {
  cursor: pointer;
  background-color: darkgray;
  color: white;
}


/**
 * Things to do when checkboxes/radios are checked
 */

.menu__checkbox__selection:checked + .menu__trigger__selection::after,
.menu__checkbox__selection[checked] + .menu__trigger__selection::after {
  transform: rotate(0deg);
}

/* This bit is something that you may see elsewhere */
.menu__checkbox__selection:checked ~ .menu__hidden,
.menu__checkbox__selection[checked] ~ .menu__hidden {
  display: block;
  visibility: visible;
  opacity: 1;
  filter: alpha(opacity=100); /* Microsoft!? */
}


/**
 * Hacky CSS only changes based off non-inline checkboxes
 * ... AKA the stuff you cannot unsee after this...
 */
.menu__checkbox__style[id="style-default"]:checked ~ .main__content {
  background-color: lightgray;
  color: black;
}

.menu__checkbox__style[id="style-default"]:checked ~ .main__content .menu__trigger__style[for="style-default"] {
  color: darkorange;
}

.menu__checkbox__style[id="style-one"]:checked ~ .main__content {
  background-color: black;
  color: lightgray;
}

.menu__checkbox__style[id="style-one"]:checked ~ .main__content .menu__trigger__style[for="style-one"] {
  color: darkorange;
}

.menu__checkbox__style[id="style-two"]:checked ~ .main__content {
  background-color: darkgreen;
  color: red;
}

.menu__checkbox__style[id="style-two"]:checked ~ .main__content .menu__trigger__style[for="style-two"] {
  color: darkorange;
}
<!--
  This bit works, but will one day cause troubles,
  but truth is you can stick checkbox/radio inputs
  just about anywhere and then call them by id with
  a `for` label. Keep scrolling to see what I mean
-->
<input type="radio"
       name="colorize"
       class="menu__checkbox__style"
       id="style-default">
<input type="radio"
       name="colorize"
       class="menu__checkbox__style"
       id="style-one">
<input type="radio"
       name="colorize"
       class="menu__checkbox__style"
       id="style-two">


<div class="main__content">

  <p class="paragraph__split">
    Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
    tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam,
    quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo
  </p>

  <input type="checkbox"
         class="menu__checkbox__selection"
         id="trigger-style-menu">
  <label for="trigger-style-menu"
         class="menu__trigger__selection"> Theme</label>

  <ul class="menu__hidden">
    <li class="menu__option">
      <label for="style-default"
             class="menu__trigger__style">Default Style</label>
    </li>

    <li class="menu__option">
      <label for="style-one"
             class="menu__trigger__style">First Alternative Style</label>
    </li>

    <li class="menu__option">
      <label for="style-two"
             class="menu__trigger__style">Second Alternative Style</label>
    </li>
  </ul>

  <p class="paragraph__split">
    consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse
    cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non
    proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
  </p>

</div>

这段代码看起来有点复杂,但是只使用CSS和HTML就可以在链接radio/checkbox inputlabel 触发器idfor属性时,从任何地方触摸和重新触摸除了body:root以外的任何东西。可能会有人展示如何重新触摸它们。

还有一个额外的注意事项是,特定idinput只能使用一个,也就是说,第一个checkbox/radio处于切换状态... 但是多个标签可以都指向同一个input,尽管这会使HTML和CSS看起来更加复杂。


...我希望有一种本地CSS Level 2的解决方法...

我不确定其他伪类,但是我记得在CSS 3之前使用:checked,如果我没记错的话,它是像[checked]这样的东西,这就是为什么你可以在上面的代码中找到它的原因。

.menu__checkbox__selection:checked ~ .menu__hidden,
.menu__checkbox__selection[checked] ~ .menu__hidden {
 /* rules: and-stuff; */
}

对于像::after:hover这样的内容,我不确定它们是在哪个CSS版本中首次出现。

总之,请不要在生产环境中使用这种方法,即使你很生气也不要。当然可以作为一个笑话,或者换句话说仅仅因为某件事情能够做到,并不意味着它应该这么做。


1
从未计划过“CSS4”,即使在2019年之前,CSS3也已经合并到了“CSS”中。 - TylerH
那些维护规范的人正在考虑采用Tyler CSS4。公告链接→https://www.w3.org/community/css4/ 讨论链接→https://github.com/w3c/csswg-drafts/issues/4770 - S0AndS0
那只是一个讨论组的名称。不会有叫做CSS4的技术。CSS模块已经独立进行版本控制多年了。正如CSSWG的贡献者在随机用户的GitHub请求中已经回复并撰写了一篇关于此的回应,这不是一个好主意。此外,该请求已经死了一年多了(您可以看到GH w3c帐户一直在删除最近的宣传评论以试图唤起它)。 - TylerH
那篇文章是一篇意见文章,总的来说,它会分散注意力,让人们偏离规范讨论线程。少数被标记为离题的评论和一个被删除的评论并不意味着重启旧话题的意图,如果工作组致力于停止CSS4讨论,我期望该问题会被标记为已关闭。在大型项目中,模块独立版本化是可以的,这是一件好事;模块版本与规范版本不冲突。 - S0AndS0
1
这是CSSWG的一位贡献成员发表的一篇观点文章,她在GitHub请求中回答了“不”,该请求已经有超过一年没有更新。可以肯定的是,它不会继续推进。正如线程中的几条评论所强调的那样,模块版本将与规范版本产生严重冲突。听着,尽管你非常希望它成为现实,但事实并非如此。重要的是要接受这一点,不要提及不存在的事物以免混淆读者。 - TylerH

3
尝试这个... 此解决方案使用纯CSS2规则,无需Javascript,在所有浏览器中均可使用。单击时,子anchor标记会激活其active伪类事件。然后它只需隐藏自己,允许active事件冒泡到父li标记,然后重新设置样式并使用新样式显示其锚定子元素。子元素已经为父元素设置了样式。
使用您的示例:
<ul>
    <li class="listitem">
        <a class="link" href="#">This is a Link</a>
    </li>
</ul>

现在使用a上的active伪类应用这些样式,以便在链接被点击时重新设置父级li标签的样式:
a.link {
    display: inline-block;
    color: white;
    background-color: green;
    text-decoration: none;
    padding: 5px;
}

li.listitem {
    display: inline-block;
    margin: 0;
    padding: 0;
    background-color: transparent;
}

/* When this 'active' pseudo-class event below fires on click, it hides itself,
triggering the active event again on its parent which applies new styles to itself and its child. */

a.link:active {
    display: none;
}

.listitem:active {
    background-color: blue;
}

.listitem:active a.link {
    display: inline-block;
    background-color: transparent;
}

现在,当您单击链接时,您应该会看到具有绿色背景的链接变为列表项的蓝色背景。

enter image description here

转向

enter image description here

点击时。


3

不,你不能仅使用CSS选择父元素。

但是由于您已经有一个.active类,将该类移动到li(而不是a)会更容易。这样您就可以仅使用CSS访问lia


3

至少在CSS3及之前的版本中,您不能像那样进行选择。 但是现在在JavaScript中很容易实现,您只需要添加一些基本的JavaScript代码即可,注意代码非常简短。

cells = document.querySelectorAll('div');
[].forEach.call(cells, function (el) {
    //console.log(el.nodeName)
    if (el.hasChildNodes() && el.firstChild.nodeName=="A") {
        console.log(el)
    };
});
<div>Peter</div>
<div><a href="#">Jackson link</a></div>
<div>Philip</div>
<div><a href="#">Pullman link</a></div>


Eduard Florinescu,你能帮忙将$('section:has(h1.heading1)').css('padding', 0);转换成纯JavaScript吗? - CodeForGood

2

目前仅在父元素内包含<input>元素时,才能根据子元素更改父元素。当输入框获得焦点时,可以使用CSS影响其相应的父元素。

以下示例将帮助您了解如何在CSS中使用:focus-within

.outer-div {
  width: 400px;
  height: 400px;
  padding: 50px;
  float: left
}

.outer-div:focus-within {
  background: red;
}

.inner-div {
  width: 200px;
  height: 200px;
  float: left;
  background: yellow;
  padding: 50px;
}
<div class="outer-div">
  <div class="inner-div">
    I want to change outer-div(Background color) class based on inner-div. Is it possible?
    <input type="text" placeholder="Name" />
  </div>
</div>


The :focus-within CSS pseudo-class matches an element if the element or any of its descendants are focused. It works with every element, not just input - LS_

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接