js行导致其上方的代码无法执行

时间:2019-01-02 18:20:32

标签: javascript html css

这是一支带有完整html的笔: https://codepen.io/froggomad/pen/WLdzoB

我正在编写2个函数-一个显示隐藏的内容,另一个隐藏它。我希望show()函数在父div上执行,并且hide()函数在使用选择器.click-text的div上执行。

但是,我将.click-text上的文本从显示切换为隐藏,所以我不希望隐藏功能始终保留在文本上。我也很希望它的交互文本在更改为隐藏功能时很明显,因此我将其设为链接。

这很好,但是当尝试将父级的onclick属性设置回show()函数时,hide块中的任何内容都不会执行。

如果删除设置父级onclick Attr的行,则脚本将按预期执行。如果我设置了另一个元素的onclick属性,脚本将按预期执行。

但是,在该行中没有任何反应,并且控制台中也没有输出指示错误。我什至设置了元素类型和类名警报,以确保我定位到正确的元素。

获取元素匹配选择器的最接近的父级:

var getClosest = function (element, selector) {
    for ( ; element && element !== document; element = element.parentNode ) {
      if ( element.matches(selector) ) return element;
    }
    return null;
}

显示隐藏的元素ul.service-category-menu

function show(elem) {    
    var menu = elem.querySelector("ul.service-category-menu"),
               click = elem.querySelector(".click-text"),
               parent = getClosest(elem, '.service-category');
    ;
    if (menu.style.display === "none" || menu.style.display === "") {
        menu.style.display = "block";
        click.innerHTML = "<a href=\"#\">Click to Hide<\/a>";
        click.setAttribute('onclick','hide(this);');
        elem.setAttribute('onclick', 'null');
    }
}

隐藏元素

function hide(elem) {    
    var parent = getClosest(elem, '.service-category'),    
                menu = parent.querySelector("ul.service-category-menu"),
                click = parent.querySelector(".click-text")
    ;
    alert(parent + "\n" + parent.className);
    //Outputs div element with expected class name (class name is unique on each div)
    if (menu.style.display === "block") {
        menu.style.display = "none";
        click.innerHTML = "Click to Show";
        click.setAttribute('onclick', 'null');
        //the above lines don't execute when the following line is in place. There's no error in console.
        parent.setAttribute('onclick','show(this)');
    }
}

3 个答案:

答案 0 :(得分:1)

它被执行,它是在您单击Click to Hide之后,事件继续到父级,并且父级的事件处理程序被执行。因此,实际发生的是(在该行中)在调用hide()之后,您无意中调用了show()

在javascript中,通常将其称为气泡(当您点击子项时,父项的点击处理程序也会在子项的点击处理程序完成后执行)。

因此,您可以在hide()函数的末尾添加此行

event.stopPropagation();

要阻止事件继续进行到父项

答案 1 :(得分:1)

如其他答案中所述设置event.stopPropagation可能会解决您的问题。另外,您可以将hide函数的最后一行更改为window.setTimeout(e => parent.setAttribute('onclick','show(this)'), 0)

现在正在发生的事情是:

  1. 您单击
  2. 它执行您的hide函数,并在该函数期间将click事件绑定到父对象。
  3. 点击会传播到父级并执行新绑定的功能,从而重新显示内容。

通过使用setTimeout(fn, 0),可以确保click事件在函数绑定到父级之前已经完成。

答案 2 :(得分:1)

首先,我必须承认我反对使用onclick属性。如果您不使用VueJS或React之类的框架,则我认为HTML和JS应该保持分离,以实现更好的控制和可维护性。

您可以使用addEventListenerremoveEventListenere.stopPropagation()来避免触发多个事件处理程序。

事件分为两个阶段:

  • 事件捕获 :事件从document一直传播到目标元素。

    • 要在此阶段捕获事件,请执行以下操作:

      elm.addEventListener('click', myFunc, true);
      
  • 事件冒泡 :事件从目标反弹回到document

    • 要在此阶段捕获事件,请执行以下操作:

      elm.addEventListener('click', myFunc, false); /* or just omit the 3rd param */
      

使用e.stopPropagation()可以中断该链。

// When the DOM is ready
window.addEventListener("DOMContentLoaded", init);

function init() {
  // Get all categories
  var $categories = document.querySelectorAll(".service-category");
  // For each of them
  Array.from($categories).forEach(function($category) {
    // Add an event listener for clicks
    $category.addEventListener("click", show);
  });
}

function getClosest(element, selector) {
  for (; element && element !== document; element = element.parentNode) {
    if (element.matches(selector)) return element;
  }
  return null;
}

function show(e) {
  var $menu  = this.querySelector("ul.service-category-menu"),
      $click = this.querySelector(".click-text");
  if (["none", ""].includes($menu.style.display)) {
    $menu.style.display = "block";
    $click.innerHTML = '<a href="#">Click to Hide</a>';
    $click.addEventListener("click", hide);
    // Remove the `show` event listener
    this.removeEventListener("click", show);
  }
  e.stopPropagation();
}

function hide(e) {
  var $parent = getClosest(this, ".service-category"),
      $menu   = $parent.querySelector("ul.service-category-menu"),
      $click  = $parent.querySelector(".click-text");
  if (!["none", ""].includes($menu.style.display)) {
    $menu.style.display = "none";
    $click.innerHTML = "Click to Show";
    $click.removeEventListener("click", hide);
    $parent.addEventListener("click", show);
  }
  e.stopPropagation();
}
.service-category{display:inline-block;border:3px solid #ccc;margin:1%;font-weight:700;font-size:3.5vw;cursor:pointer;background-color:#fff;z-index:3;background-position:center;background-size:cover;color:#000}.click-text{text-align:right;font-size:1.25vw;font-style:italic;font-weight:700;padding-right:1%}.service-category:hover .click-text{color:#b22222}.service-category-menu{display:none;margin-left:8%;margin-right:8%;margin-top:1%;background-color:#fff;font-weight:700;font-size:1.6vw;border-radius:10px}
<div class="service-category web-back" id="web-back">
  <div class="row-overlay">
    Web <br /> Development
    <div class="click-text">Click to Show</div>
    <ul class="service-category-menu web">
      <li>
        Some text...
      </li>
    </ul>
  </div>
</div>

<div class="service-category web-front" id="web-front">
  <div class="row-overlay">
    Web <br /> Design
    <div class="click-text">Click to Show</div>
    <ul class="service-category-menu web">
      <li>
        Some text...
      </li>
    </ul>
  </div>
</div>