将jQuery的.on(' mouseenter')重写为纯JS变体

时间:2017-10-12 17:03:46

标签: javascript jquery

我有一个包含其他div元素的容器div:

<div id="container">
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
</div>

如何在纯JS中重写以下jQuery?

$('#container').on('mouseenter', 'div', myFunction)

也就是说,我怎样才能听哪个鼠标悬停在它上面以便应用myFunction?我已尝试过以下操作,但这只适用于myFunction到容器div:

&#13;
&#13;
container.addEventListener('mouseenter', function(event) {
  let hoveredElement = event.target;
  if (hoveredElement.tagName === 'DIV') {
    myFunction(hoveredElement);
  }
});
&#13;
#container { width: 200px; }
#container > div  { height: 100px; background-color: red; }
&#13;
<div id="container">
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
</div>
&#13;
&#13;
&#13;

我也试过了event.currentTarget,但它仍然会给出相同的结果。

P.S。我不想把事件监听器放在容器div的子节点上,因为有很多孩子。

编辑:我已删除console.log(hoveredElement);这是一条调试线。

3 个答案:

答案 0 :(得分:1)

我在Chrome上调试了jQuery, jQuery mouseenter事件实际上是在幕后使用mouseover事件。

在jQuery源代码4095行中,每次触发事件时都会运行一行:

jQuery.event.dispatch.apply( elem, arguments );

如果您暂停并检查arguments,您会发现它是类型为mouseover的MouseEvent。

哪种有意义,因为您使用了jQuery的委托事件处理程序语法,而jQuery足够聪明,可以用mouseenter替换mouseover which doesn't bubble and doesn't trigger as you move through the event handler's descendents which does fire when you move through the listener's children

所以,你的解决方案基本上就是在做jQuery正在做的事情 - 使用mouseover

&#13;
&#13;
container.addEventListener('mouseover', function(event) {
  console.log("mouseover target", event.target);
  console.log("mouseover currentTarget", event.currentTarget);
});

container.addEventListener('mouseenter', function(event) {
  console.log("mouseenter target", event.target);
  console.log("mouseenter currentTarget", event.currentTarget);
});

$('#container').on('mouseenter', '.child', function(event) {
  console.log('jquery mouseenter target', event.target);
  console.log('jquery mouseenter currentTarget', event.currentTarget);
  console.log('jquery mouseenter delegateTarget', event.delegateTarget);
});
&#13;
.child {
  border: 1px solid #007;
  height: 50px;
}
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.js"></script>

<div id="container">
  <div class="child"></div>
  <div class="child"></div>
  <div class="child"></div>
</div>
&#13;
&#13;
&#13;

答案 1 :(得分:0)

您需要从e.target开始,并遍历祖先,在您找到的每个div上调用处理程序。当你到达绑定元素时,你停止遍历祖先。

同时将事件类型更改为mouseover,因为mouseenter不会冒泡,因此event.target始终是绑定元素。

const container = document.querySelector("#container");

container.addEventListener('mouseover', function(event) {
  let el = event.target;
  do {
    if (el.tagName === 'DIV') {
      myFunction.call(el, event);
    }
  } while((el = el.parentNode) !== this);
});

function myFunction(event) {
  console.log(this.id);
}
<div id=container>
  <section>
    <div id=foo>
      <p>Hover me to invoke the function</p>
    </div>
    <p>This one does not invoke</p>
  </section>
</div>

没有注意到你有小写"div",所以我也大写了。

请注意,为方便起见,您可以将其抽象为可重用的函数。您还可以使用CSS选择器和.matches()使其更通用,而不是仅限于标记名称比较。

const container = document.querySelector("#container");

delegate("mouseenter", container, "div", myFunction);

function myFunction(event) {
  console.log(this.id);
}


// Reusable event delegation maker
function delegate(type, container, selector, handler) {
  if (type === "mouseenter") {
    type = "mouseover";
  } else if (type === "mouseleave") {
    type = "mouseout";
  }

  container.addEventListener(type, function(event) {
    let el = event.target;
    do {
      if (el.matches(selector)) {
        handler.call(el, event);
      }
    } while ((el = el.parentNode) !== this);
  });
}
<div id=container>
  <section>
    <div id=foo>
      <p>Hover me to invoke the function</p>
    </div>
    <p>This one does not invoke</p>
  </section>
</div>

答案 2 :(得分:0)

您要做的是一种称为事件委派的做法。鉴于今天的JavaScript状态,这很容易做到。要复制JavaScript语法,我习惯于创建一个可以使用的可返回对象。

使用$get( query )获取要执行委派的元素。在那里,您使用on方法和参数(event, query for child elements, function)

当您为on方法编写函数时,它将按照惯例传递event参数,但下一个参数将是元素的索引,在此案件,鼠标过度。

参数没有安全保护,因为这不是问题的一部分,而且可以通过一点点肘部油脂轻松解决:)

let $get = (query) => {
  if (document.querySelector(query)) {
    let returnableObj = {
      ele: document.querySelector(query),
      list(q) {
        return Array.from(this.ele.querySelectorAll(q))
      },
      getIndex(ele) {
        return function(q) {
          let foundIndex;
          returnableObj.list(q).filter((itm, ind) => {
            let bool = ele.isSameNode(itm);
            if (bool) foundIndex = ind;
            return bool;
          });
          return foundIndex >= 0 ? foundIndex : false;
        }
      }
    }
    returnableObj.on = function(evt, childquery, fn) {
      this.ele.addEventListener(evt, function(e) {
        if (e.target.matches(childquery)) {
          let myIndex = returnableObj.getIndex(e.target)(childquery);
          fn(e, myIndex);
        }
      });
    }
    return returnableObj;
  } else alert('invalid query');
}

let $get = (query) => {
  if (document.querySelector(query)) {
    let returnableObj = {
      ele: document.querySelector(query),
      list(q) {
        return Array.from(this.ele.querySelectorAll(q))
      },
      getIndex(ele) {
        return function(q) {
          let foundIndex;
          returnableObj.list(q).filter((itm, ind) => {
            let bool = ele.isSameNode(itm);
            if (bool) foundIndex = ind;
            return bool;
          });
          return foundIndex >= 0 ? foundIndex : false;
        }
      }
    }
    returnableObj.on = function(evt, childquery, fn) {
      this.ele.addEventListener(evt, function(e) {
        if (e.target.matches(childquery)) {
          let myIndex = returnableObj.getIndex(e.target)(childquery);
          fn(e, myIndex);
        }
      });
    }
    return returnableObj;
  } else alert('invalid query');
}



let ele = $get("#mydiv");
ele.on("mouseover", ".child", function(e, ind) {
  alert('Peanut Butter! Child ' + (ind+1));
})
h1,h2,h3 {
width: 200px;
}
<div id="mydiv">
  <span>hiya</span>
  <h1 class="child"> child 1 want some candy?</h1>
  <h2 class="child"> child 2 I choose you, spirit dragon</h2>
  <h3 class="child"> child 3, Scoreboard  </h3>
</div>