相邻兄弟选择器 element.querySelector("+element") 无效

Adjacent sibling selector element.querySelector("+element") is invalid

这个简单的代码可以工作:

<!DOCTYPE html>
<html>
<body>
    <br>
    <div>
        <a href="#a1">Link 1</a>
        <a href="#a2">Link 2</a>
        <a href="#a3">Link 3</a>
        <a href="#a4">Link 4</a>
    </div>
    <a href="#c1">Link 1 <span>color me plz!</span></a>
    <br><br>
    <div>
        <a href="#b1">Link 1</a>
        <a href="#b2">Link 2</a>
        <a href="#b3">Link 3</a>
        <a href="#b4">Link 4</a>
    </div>
    <a href="#c1">Link 1 <span>color me plz!</span></a>
    <a href="#c2">Link 2 <span>color me plz!</span></a>

    <script>
        testing = document.querySelectorAll("div + a");
        for (let i = 0; i < testing.length; i++){
            testing[i].addEventListener("mouseenter", function(){
                const x = testing[i];
                x.style.backgroundColor = "cyan";
                x.querySelector("span").style.backgroundColor = "pink";
            });
        }
    </script>
</body>
</html>

但我真正的问题是:

<!DOCTYPE html>
<html>
<head>
    <link rel="stylesheet" href="style-test.css">
</head>
<body>
    <nav>
        <div>
            <a href="#A1">Menu A.1</a>
            <a href="#A2">Menu A.2</a>
        </div>
        <div class="right">
            <div>
                <a href="#B1">Menu B.1</a>
                <ul>
                    <li><a href="#B1.1">B.1 Sub 1</a></li>
                    <li><a href="#B1.2">B.1 Sub 2</a></li>
                </ul>
            </div>
            <div>
                <a href="#B2"">Menu B.2</a>
                <ul>
                    <li><a href="#B2.1">B.2 Sub 1</a></li>
                    <li><a href="#B2.2">B.2 Sub 2</a></li>
                </ul>
            </div>
            <div>
                <a href="#B3">Menu B.3</a>
                <ul>
                    <li><a href="#B3.1">B.3 Sub 1 Test Longer Text</a></li>
                    <li><a href="#B3.2">B.3 Sub 2</a></li>
                </ul>
            </div>
            <div>
                <a href="#B4">Menu B.4</a>
                <ul>
                    <li><a href="#B4.1">B.4 Sub 1</a></li>
                    <li><a href="#B4.2">B.4 Sub 2</a></li>
                </ul>
            </div>
        </div>
    </nav>

    <div style="float: left; margin-top: 1000px"></div>

    <script>
        const menu = document.querySelectorAll("nav div > a");
        console.log("Get all menu:");
        console.log(menu);
        for (let i = 0; i < menu.length; i++) {
            menu[i].addEventListener("mouseenter", function(){
                console.log("mouseEnter");
                console.log("menu[i].addEventListener:");
                console.log(menu[i]);

                //console.log(menu[i].querySelector("+ ul"));
                const subMenu = menu[i].querySelector(` + ul`);
                /* ^^^ The problem is here above ^^^ --- Everything stops here */
                console.log("OK! 'querySelector' is valid"); //<-- this won't display...

                //if(window.getComputedStyle(subMenu).getPropertyValue("display") === "block") /*Corrected*/
                if(subMenu.style.display === "block")
                {
                    console.log("subMenu.style.display === block");
                    const subMenuBox = subMenu.getBoundingClientRect();
                    const posX = (subMenuBox.left + subMenuBox.width) - window.innerWidth;
                    if(posX > 0)
                        subMenu.style.left = (-posX - 20) + "px";
                        /*padding problem (need -20): didn't .getBoundingClientRect() include padding?*/
                        // /*or just*/ subMenu.style.right = "0px";
                    console.log("When here successfully!");
                }
                else{
                    console.log("Failed...");
                }
            })
        }
    </script>
</body>
</html>

css:

body{
    color: #fff;
    font-size: 20px;
    background: #999;
    margin: 0;
}
a{
    color: #cfc;
}
nav, nav div > a, nav div{
    float: left;
}
nav{
    width: 100%;
    background: #4169E1;
    white-space: nowrap;
}
nav div{
    margin: 0em 0.2em;
}
nav div:first-child{
    margin-left: 0;
}
nav div div{
    position: relative;
    margin: 0;
}
nav a{
    color: #cff;
    display: block;
    font-weight: 600;
    text-decoration: none;
    padding: 0.5em 1em;
}
nav a:hover{
    color: #fff;
    background: #f90;
}
nav div > a + ul{
    position: absolute;
    top: 2.15em;
    float: left;
    background: #666;
    list-style-type: none;
    margin: 0;
    padding: 0;
    min-width: 180px;
    display: none;
}
nav div:hover > a + ul{
    display: block;
}
.right{
    float: right;
}

我只想得到<a>紧挨着的兄弟,<a>之后的<ul>,也就是css中的a + ul

<div>
    <a href="#B1">Menu B.1</a>
    <ul>
        <li><a href="#B1.1">B.1 Sub 1</a></li>
        <li><a href="#B1.2">B.1 Sub 2</a></li>
    </ul>
</div>

关注这里:

<script>
    const menu = document.querySelectorAll("nav div > a");

    for (let i = 0; i < menu.length; i++) {
        menu[i].addEventListener("mouseenter", function(){

            //Problem is here...
            const subMenu = menu[i].querySelector(" + ul");

            /*Corrected*/
            //if(window.getComputedStyle(subMenu).getPropertyValue("display") === "block")
            if(subMenu != null && subMenu.style.display === "block")
            {
                const subMenuBox = subMenu.getBoundingClientRect();
                const posX = (subMenuBox.left + subMenuBox.width) - window.innerWidth;
                if(posX > 0)
                    subMenu.style.left = (-posX -20) + "px";
                    /*padding problem (need -20): didn't .getBoundingClientRect() include
                    padding?*/
                    // /*or just*/ subMenu.style.right = "0px";
            }
        })
    }
</script>

现在,我在这里做了什么: 首先,我select所有<a>里面的<nav>。 然后使用 for() 循环并将 mouseenter 事件放入所有 selected <a>。 当用户将鼠标悬停在 <a> 上时,mouseenter 确切知道悬停在哪个 <a> 上。 现在问题来了:我想 select 悬停 <a>.

a + ul

我试过了:

console.log(document.querySelector("menu[i] + ul"));

给我一个 null

console.log(document.querySelector(menu[i] + " + ul"));

给我 SyntaxError: 'file:///C:/Users/path/path/thisPage.html + ul' is not a valid selector

console.log(menu[i].querySelector(" + ul"));

给我 SyntaxError: '+ul' is not a valid selector

我该如何解决这个问题?使用 .querySelector() 继续 selection 但使用相邻的同级标签的正确做法是什么?

一方面,在非古代浏览器上,您可以使用 :scope 来指示调用 querySelector 的元素。但是 querySelector select 个元素,这些元素是当前元素的 children,而 <ul> 你想要一个兄弟姐妹

取元素的 nextElementSibling 代替,检查它是否存在,检查它是否是 ul 代替:

for (const menu of document.querySelectorAll("nav div > a")) {
  menu.addEventListener("mouseenter", function() {
    console.log(menu, menu.nextElementSibling);
    
    const subMenu = menu.nextElementSibling;
    if (!subMenu || !subMenu.matches('ul')) {
      return;
    }

    if (subMenu.style.display === "block") {
      console.log("subMenu.style.display === block");
      const subMenuBox = subMenu.getBoundingClientRect();
      const posX = (subMenuBox.left + subMenuBox.width) - window.innerWidth;
      subMenu.style.left = -posX;
      console.log("When here successfully!");
    } else {
      console.log("Failed...");
    }
  })
}
body {
  color: #fff;
  font-size: 20px;
  background: #999;
  margin: 0;
}

a {
  color: #cfc;
}

nav,
nav div>a,
nav div {
  float: left;
}

nav {
  width: 100%;
  background: #4169E1;
  white-space: nowrap;
}

nav div {
  margin: 0em 0.2em;
}

nav div:first-child {
  margin-left: 0;
}

nav div div {
  position: relative;
  margin: 0;
}

nav a {
  color: #cff;
  display: block;
  font-weight: 600;
  text-decoration: none;
  padding: 0.5em 1em;
}

nav a:hover {
  color: #fff;
  background: #f90;
}

nav div>a+ul {
  position: absolute;
  top: 2.15em;
  float: left;
  background: #666;
  list-style-type: none;
  margin: 0;
  padding: 0;
  min-width: 180px;
  display: none;
}

nav div:hover>a+ul {
  display: block;
}

.right {
  float: right;
}
<nav>
  <div>
    <a href="#A1">Menu A.1</a>
    <a href="#A2">Menu A.2</a>
  </div>
  <div class="right">
    <div>
      <a href="#B1">Menu B.1</a>
      <ul>
        <li><a href="#B1.1">B.1 Sub 1</a></li>
        <li><a href="#B1.2">B.1 Sub 2</a></li>
      </ul>
    </div>
    <div>
      <a href="#B2">Menu B.2</a>
      <ul>
        <li><a href="#B2.1 ">B.2 Sub 1</a></li>
        <li><a href="#B2.2 ">B.2 Sub 2</a></li>
      </ul>
    </div>
    <div>
      <a href="#B3 ">Menu B.3</a>
      <ul>
        <li><a href="#B3.1 ">B.3 Sub 1 Test Longer Text</a></li>
        <li><a href="#B3.2 ">B.3 Sub 2</a></li>
      </ul>
    </div>
    <div>
      <a href="#B4 ">Menu B.4</a>
      <ul>
        <li><a href="#B4.1 ">B.4 Sub 1</a></li>
        <li><a href="#B4.2 ">B.4 Sub 2</a></li>
      </ul>
    </div>
  </div>
</nav>

<div style="float: left; margin-top: 1000px "></div>

因为并非所有菜单都有子菜单,所以您需要先测试 UL 是否存在,然后再尝试使用它。

请注意,subMenu.style.display === "block" 永远不会在给定的代码中实现,因为 <ul> 没有 style 属性 直接在元素上 .如果他们这样做了,测试就会成功。如果您要查看它们当前是否 正在显示,请改用 window.getComputedStyle

for (const menu of document.querySelectorAll("nav div > a")) {
  menu.addEventListener("mouseenter", function() {
    console.log(menu, menu.nextElementSibling);
    
    const subMenu = menu.nextElementSibling;
    if (!subMenu || !subMenu.matches('ul')) {
      return;
    }

    if (window.getComputedStyle(subMenu).display === "block") {
      console.log("subMenu.style.display === block");
      const subMenuBox = subMenu.getBoundingClientRect();
      const posX = (subMenuBox.left + subMenuBox.width) - window.innerWidth;
      subMenu.style.left = -posX;
      console.log("When here successfully!");
    } else {
      console.log("Failed...");
    }
  })
}
body {
  color: #fff;
  font-size: 20px;
  background: #999;
  margin: 0;
}

a {
  color: #cfc;
}

nav,
nav div>a,
nav div {
  float: left;
}

nav {
  width: 100%;
  background: #4169E1;
  white-space: nowrap;
}

nav div {
  margin: 0em 0.2em;
}

nav div:first-child {
  margin-left: 0;
}

nav div div {
  position: relative;
  margin: 0;
}

nav a {
  color: #cff;
  display: block;
  font-weight: 600;
  text-decoration: none;
  padding: 0.5em 1em;
}

nav a:hover {
  color: #fff;
  background: #f90;
}

nav div>a+ul {
  position: absolute;
  top: 2.15em;
  float: left;
  background: #666;
  list-style-type: none;
  margin: 0;
  padding: 0;
  min-width: 180px;
  display: none;
}

nav div:hover>a+ul {
  display: block;
}

.right {
  float: right;
}
<nav>
  <div>
    <a href="#A1">Menu A.1</a>
    <a href="#A2">Menu A.2</a>
  </div>
  <div class="right">
    <div>
      <a href="#B1">Menu B.1</a>
      <ul>
        <li><a href="#B1.1">B.1 Sub 1</a></li>
        <li><a href="#B1.2">B.1 Sub 2</a></li>
      </ul>
    </div>
    <div>
      <a href="#B2">Menu B.2</a>
      <ul>
        <li><a href="#B2.1 ">B.2 Sub 1</a></li>
        <li><a href="#B2.2 ">B.2 Sub 2</a></li>
      </ul>
    </div>
    <div>
      <a href="#B3 ">Menu B.3</a>
      <ul>
        <li><a href="#B3.1 ">B.3 Sub 1 Test Longer Text</a></li>
        <li><a href="#B3.2 ">B.3 Sub 2</a></li>
      </ul>
    </div>
    <div>
      <a href="#B4 ">Menu B.4</a>
      <ul>
        <li><a href="#B4.1 ">B.4 Sub 1</a></li>
        <li><a href="#B4.2 ">B.4 Sub 2</a></li>
      </ul>
    </div>
  </div>
</nav>

<div style="float: left; margin-top: 1000px "></div>