Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

This simple code will work:

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

But my real problem is this:

<!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;
}

I just want to get the sibling of <a> just next to it, the <ul> after <a>, which is a + ul in css

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

Focus here:

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

Now, what I done here: First, I select all the <a> inside <nav>. Then using for() loop and put mouseenter event to all the selected <a>. When user hover over the <a>, the mouseenter know exactly which <a> was hover. Now here's the problem: I want to select the a + ul of hovered <a>.


I have tried this:

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

gives me a null value

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

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

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

gives me SyntaxError: '+ul' is not a valid selector

How can I fix this? What is the right thing to do with .querySelector() for continuing selection but with adjacent sibling tag?

like image 381
Bakunawa Avatar asked Jun 16 '26 22:06

Bakunawa


1 Answers

On one hand, on non-ancient browsers, you can use :scope to indicate the element on which the querySelector is being called on. But querySelector will only select elements which are children of the current element, and the <ul> you want is a sibling.

Take the nextElementSibling of the element instead, check that it exists, and check that it's a ul instead:

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>

Because not all menus have sub-menus, you do need to test if the UL exists first before trying to do stuff with it.

Note that subMenu.style.display === "block" is never fulfilled in the given code because the <ul>s don't have style properties directly on the elements. If they did, the test would succeed. If you're trying to see if they currently are being displayed, use window.getComputedStyle instead:

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>
like image 71
CertainPerformance Avatar answered Jun 19 '26 11:06

CertainPerformance



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!