首页 > 解决方案 > 在父容器的 mouseover 和 mouseleave 上切换 preventDefault()

问题描述

我有一些容器,里面有包含链接的子容器。

发生的情况是,当用户将鼠标悬停在父容器上以显示子容器时,链接在前 2 秒内被禁用。如果用户在单击链接之前将鼠标移开,则此行为将使用“hasBeenHovered”变量重置,该变量在 mouseleave 事件中从 true 变为 false。

我面临的两个问题是:

a)我不能让它只在被悬停的父级上工作 - 它遍历所有这些并显示它们;

b)在移动设备上,无论如何都会将不透明度恢复为 1 并通过重新点击再次禁用链接(因此重新点击有效地作为 mouseleave 事件工作?)。如果这非常复杂,我可能只是拥有它,因此它一直可见,直到父容器到达视口的顶部。

虽然代码沙箱在下方,但它显示“未捕获的 TypeError:allowLinks 不是函数”,但在 CodePen 上演示是否有效?

非常感谢您的帮助。

艾米丽

代码笔: https ://codepen.io/emilychews/pen/rNjXMee

var parent = document.querySelectorAll(".parent");
var child = document.querySelectorAll(".child");
var link = document.querySelectorAll(".link");

var hasBeenHovered = false;

var listener = function (e) {
  e.preventDefault();
};

// prevent default on all specific links
link.forEach(function (item) {
  item.addEventListener("click", listener);
});

// mouseover that changes opacity to 1 and removes prevent default on links
parent.forEach(function (item) {
  item.addEventListener("mouseover", function (event) {
      child.forEach(function (item) {
        item.style.opacity = "1";
        item.style.transition = "opacity .5s";
      });

      // remove prevent Default
      if (hasBeenHovered === false) {
        function allowLinks() {
          link.forEach(function (item) {
            item.removeEventListener("click", listener);
            hasBeenHovered = true;
          });
        }
      }

      setTimeout(function () {
        allowLinks();
      }, 2000);
    
    }, false );
  
});

// mouseleave event re-adds opacity: 0 and re-adds prevent default
parent.forEach(function (item) {
  item.addEventListener("mouseleave", function (event) {
      child.forEach(function (item) {
        item.style.opacity = "0";
        item.style.transition = "opacity .5s";
      });

      // re-add prevent Default
      if (hasBeenHovered === true) {
        link.forEach(function (item) {
          item.addEventListener("click", listener);
          hasBeenHovered = false;
        });
      }
    }, false );
  
});
body {
  margin: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100vh;
}

.parent {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: 20rem;
  height: 20rem;
  background: lightblue;
  margin: 1rem;
}

.child {
  opacity: 0; /* hides child container*/
  padding: 1rem;
  background: red;
}

a {
  color: #fff;
  display: block;
  margin: 1rem 0;
}
<div class="parent">
  <div class="child">
    <a target="_blank" href="https://google.com" class="link">This will work after 2 seconds of mouseover</a>
    <a target="_blank" href="https://google.com" class="link">This will work after 2 seconds of mouseover</a>
  </div>
</div>

<div class="parent">
  <div class="child">
    <a target="_blank" href="https://google.com" class="link">This will work after 2 seconds of mouseover</a>
    <a target="_blank" href="https://google.com" class="link">This will work after 2 seconds of mouseover</a>
  </div>
</div>

标签: javascripteventsmouseevent

解决方案


为了保留您的大部分原始代码逻辑,我已经修改了很多,但这应该是您的目标。对于移动部分,我建议为触摸处理程序设置一个标志,但请注意它会变得更加复杂,因为移动设备也会响应onclick处理程序。

由于 StackOverflow 安全性,这些链接在代码段中不起作用,因此添加了控制台日志,但如果您复制到 CodePen,这些链接将起作用

const parents = document.querySelectorAll(".parent");

parents.forEach(parent => {
  const children = parent.querySelectorAll(".child");
  const links = parent.querySelectorAll(".link");

  let timeoutId = null; // track the timeout so we can clear it
  let enableLinks = false; // should we allow links?

  links.forEach(link =>
    link.addEventListener("click", evt => {
      if (!enableLinks) {
        evt.preventDefault(); // hold them hostage
      } else {
        console.log('StackOverflow prevents links'); // just a placeholder for SO snippet
      }
    }, true)
  );

  parent.addEventListener("mouseover", function() {
    enableLinks = false; // ensure links are disabled at first
    children.forEach(child => child.style.opacity = "1"); // let the children be seen
    if (!timeoutId) // make sure there isn't already a timeout
      timeoutId = setTimeout(() => enableLinks = true, 2000); // enable links after the 2s
  }, false);

  parent.addEventListener("mouseleave", function(event) {
    children.forEach(child => child.style.opacity = "0"); // hide your children
    clearTimeout(timeoutId); // remove the timeout so it can't overlap
    timeoutId = null; // clear timeout id
    enableLinks = false; // turn off links
  }, false);
});
body {
  margin: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100vh;
}

.parent {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: 20rem;
  height: 20rem;
  background: lightblue;
  margin: 1rem;
}

.child {
  opacity: 0;
  /* hides child container*/
  padding: 1rem;
  background: red;
  transition: opacity .5s;
}

a {
  color: #fff;
  display: block;
  margin: 1rem 0;
}
<div class="parent">
  <div class="child">
    <a target="_blank" href="https://google.com" class="link">This will work after 2 seconds of mouseover</a>
    <a target="_blank" href="https://google.com" class="link">This will work after 2 seconds of mouseover</a>
  </div>
</div>

<div class="parent">
  <div class="child">
    <a target="_blank" href="https://google.com" class="link">This will work after 2 seconds of mouseover</a>
    <a target="_blank" href="https://google.com" class="link">This will work after 2 seconds of mouseover</a>
  </div>
</div>


推荐阅读