Селектор соседнего брата element.querySelector ("+ element") недопустим - PullRequest
0 голосов
/ 01 февраля 2020

Этот простой код будет работать:

<!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> рядом с ним, <ul> после <a>, что составляет a + ul в 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>

Фокус здесь:

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

Теперь, что я сделал здесь: во-первых, я выбираю все <a> внутри <nav>. Затем с помощью for() l oop и поставить mouseenter событие на все выбранные <a>. Когда пользователь наводит курсор на <a>, mouseenter точно знает, какой <a> был наведен. Теперь вот проблема: я хочу выбрать a + ul hovered <a>.


Я пробовал это:

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() для продолжения выбора, но с соседним тегом родного брата?

1 Ответ

1 голос
/ 01 февраля 2020

С одной стороны, в не древних браузерах вы можете использовать :scope, чтобы указать элемент, для которого вызывается querySelector. Но querySelector будет только выбирать элементы, которые являются дочерними текущего элемента, а желаемым <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>

Поскольку не все меню имеют подменю, вам do необходимо проверить, существует ли UL сначала, прежде чем пытаться выполнить разберитесь с ним.

Обратите внимание, что subMenu.style.display === "block" никогда не выполняется в данном коде, потому что <ul> s не имеет 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>
...