Good day.
I don't know the right terminology about my question but what I want to ask is how to ask is this:
//functionA
$(document).on('click', '.classA1, .classA2, .classA3', function(event) {
//i want to call the functionB when functionA is used.
//if possible, when I use the onclick on classB2
}
//functionB
$(document).on('click', '.classB1, .classB2', function(event) {}
note, I am new to JavaScript and was suddenly given a task by my boss. I cant really ask them about this since they are also new to the project/JavaScript since we just received the project from previous developer.
$('.classA1, .classA2, .classA3').on('click', ()=> {
console.log('function A called');
$('.classB1, .classB2').trigger('click');
});
$('.classB1, .classB2').on('click', ()=> {
console.log('function B called');
});
a {
display: inline-block;
background: teal;
padding: .5rem;
border-radius: 5px;
color: white;
font: 16px Arial, sans-serif;
cursor: pointer;
}
.b {
background: orange;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<a class="classA1">class A1</a>
<a class="classA2">class A2</a>
<a class="classA3">class A3</a>
<a class="classB1 b">class B1</a>
<a class="classB2 b">class B2</a>
If I would be thinking simply about it, I would declare two methods first and then use them as callbacks in the listeners.
function functionA(event) {
// implement your logic here
// and call functionB as well
functionB(event);
}
function functionB(event) {
// implement your B logic here
}
$(document).on('click', '.classA1, .classA2, .classA3', functionA);
$(document).on('click', '.classB1, .classB2', functionB);
This is actually not much else to be done as you can't delegate an event in such way as you wish it.
Imagine following scenario. Pink rectangle would be e.g. a span within a div (black rectangle) If you would add a click listener to the "div", then the click event would fire when you click the "div" and even if you click the "span". The click event propagated.
Otherwise you might want to add multiple click listeners to the same object. Then depending on your desire you can either interrupt the propagation (blocking the other listeners) by e.g. invoking the event.stopPropagation() method.
I would like the community to add more explanations if I missed something critical.
I'm supposed to clone some elements with Jquery and it works well but when i delete the first element which i'm cloning the others with it, after that the new cloned elements don't have the events which supposed to have!
i tried .clone(true, true) method and it clone event but not after the deleting the first element.
var card = $(".newCard"); //class name of first element
$("#addBtn0").click(function() {
$(".row").append(card.clone(true, true)); //it works well but...
});
$("[class^=btnDelete]").click(function() {
$(this).closest(".newCard").remove(); //it works too but not after deleting first element and creating again
});
I don't know why this is happening, actually every element should have the events even after deleting the first element and recreate.
The problem is the click event is being bound to that first element, and as a result that binding is removed along with the element. When dealing with dynamic elements you should use event delegation by using the .on method on a static element. Such as the document itself.
EDIT: You won't notice any performance issues on a small document like this, but using the document as your event delegator can cause performance issues on larger documents. You can read more about event delegation performance here.
var card = $(".newCard");
$("#addBtn0").click(function() {
$(".row").append(card.clone(true, true));
});
$(document).on('click', '.btnDelete', function() {
$(this).closest(".newCard").remove();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button id="addBtn0">Add</button>
<div class="row">
<div class="newCard">Card <button class="btnDelete">Delete</button></div>
</div>
This happens because of
$("[class^=btnDelete]").click(function() {
the above line will target the existing (!!!) element and it's inner button.
Since you're cloning that existing element, you're also cloning the Event bound to it's button on-creation.
Once you delete that card (stored in variable), you're also destroying the Event bound to it.
To fix that issue use .on() with dynamic event delegation:
$(".row").on('click', '[class^=btnDelete]', function() {
var card = $(".newCard"); //class name of first element
$("#addBtn0").click(function() {
$(".row").append(card.clone(true, true));
});
$(".row").on('click', '[class^=btnDelete]', function() {
$(this).closest(".newCard").remove();
});
<div class="row">
<div class="newCard">CARD <button class="btnDelete">DELETE</button></div>
</div>
<button id="addBtn0">ADD</button>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
https://api.jquery.com/on/#direct-and-delegated-events
Other issues and solution
Other issues you have in your code are mainly naming stuff. [class^=btnDelete] is just waiting for you one day adding a style class to that poor button and see your JavaScript fail miserably. Also, why btnAdd0 what's the 0? Why .clone(true, true) at all?
Here's a better rewrite:
const $Cards = $('.Cards');
const $Cards_add = $('.Cards-btn--add');
const $Cards_item = (html) => {
const $self = $('<div/>', {
appendTo: $Cards,
class: `Cards-item`,
html: html || `New Card`,
});
$('<button/>', {
appendTo: $self,
class: `Cards-btn Cards-btn--delete`,
text: `Delete`,
on: {
click() {
$self.remove();
}
},
});
}
let card_id = 0;
$Cards_add.on('click', () => $Cards_item(`This is Card ${++card_id}`));
// Create some dummy cards
$Cards_item(`This is Card ${++card_id}`);
$Cards_item(`This is Card ${++card_id}`);
$Cards_item(`This is Card ${++card_id}`);
/**
* Cards component styles
*/
.Cards {} /* scss extend .row or rather define styles directly */
.Cards-item { padding: 5px; margin: 5px 0; background: #eee; }
.Cards-btn { }
.Cards-btn--add { color: blue; }
.Cards-btn--delete { color: red; }
<div class="Cards"></div>
<button class="Cards-btn Cards-btn--add">ADD</button>
<script src="//code.jquery.com/jquery-3.4.1.js"></script>
$(".row").append(card.clone(true, true));
You are still using the original result of $('.newCard') that does not include any new cards you've added.
$(".row").append($(this).parent().clone(true, true));
this works
There are a bunch of div elements on the page.
I have a nested div inside of them.
I want to be able to add a class to the clicked element, and .show() the child div.
$('.container').on('click', function(){
$(this).toggleClass('red').children('.insideItem').slideToggle();
});
I can click on it, it drops down.
Click again, it goes away.
So, now I need some method to removeClass() and slideUp() all of the other ones in the event of a click anywhere except the open div. Naturally, I tried something like this:
$('html').on('click', function(){
$('.container').removeClass('red').children('div').slideUp();
});
Well, that just stops the effect from staying in the first place. I've read around on event.Propagation() but I've read that should be avoided if possible.
I'm trying to avoid using any more prebuilt plugins like accordion, as this should be a pretty straightforward thing to accomplish and I'd like to know a simple way to make it work.
Would anyone be able to show a quick example on this fiddle how to resolve this?
Show only one active div, and collapse all others if clicked off
https://jsfiddle.net/4x1Lsryp/
One way to go about it is to update your code with the following:
1) prevent the click on a square from bubbling up to the parent elements
2) make sure to reset the status of all the squares when a new click is made anywhere.
$('.container').on('click', function(){
$this = $(this);
$('.container').not($this).removeClass('red').children('div').slideUp();
$this.toggleClass('red').children('div').slideToggle();
return false;
});
See the updated JSfiddle here: https://jsfiddle.net/pdL0y0xz/
You need to combine your two approaches:
for (var i = 0; i < 10; i++) {
$('#wrap').append("<div class='container'>" + i + "<div class='insideDiv'>Inside Stuff</div></div>");
}
$('.container').on('click', function() {
var hadClassRed = $(this).hasClass('red');
$('.container').removeClass('red').children('div').slideUp();
if (!hadClassRed) {
$(this).toggleClass('red').children('div').slideToggle();
}
});
.container {
width: 200px;
height: 200px;
float: left;
background: gray;
margin: 1em;
}
.insideDiv {
display: none;
}
.red {
background: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="wrap"></div>
I was hoping to get some help. I set up a JSfiddle below:
http://jsfiddle.net/y7mEY/102/
I was looking to have the grey div box that appears when the input box is clicked (that will have the search options in it) to close when I click anywhere outside it.
I would like to not load jquery and therefore keep everything in javascript.
Any help?
code below:
HTML
<div id = "searchHousing">
<input type="text" name="value" id="fillIn">
</div>
CSS
.searchBox {
position: absolute;
font-family:Arial;
font-size: 10pt;
color:black;
height: 200px;
width: 200px;
padding-left: 3px;
padding-top: 1px;
border: 1px solid white;
background-color: grey;
}
#fillIn {
width: 200px;
height: 28px;
border-radius: 4px;
font-size: 16px;
margin-bottom: 2px;
#searchHousing {
float: left;
}
Javascript:
var inputSearch = document.getElementById('fillIn');
inputSearch.onclick = function() {
var searchBox = document.createElement("div");
searchBox.className = "searchBox";
document.getElementById('searchHousing').appendChild(searchBox);
};
Thanks,
Ewan
I would suggest to not create new 'div' on click event. This will result in multiple "searchBox" divs when the input box is clicked multiple times. Needless to say, it will require cleanup effort to remove duplicated divs.
Rather, create the searchBox div in HTML and toggle it's visibility on javascript events.
Updated JSFiddle: http://jsfiddle.net/y7mEY/109/
[
var inputSearch = document.getElementById('fillIn');
inputSearch.onclick = function() {
document.getElementById('searchBox').style.display = 'block';
};
inputSearch.onblur = function() {
document.getElementById('searchBox').style.display = 'none';
};
]
If you want to use your current approach, simply use the "onblur" event to remove the element when the element loses focus.
inputSearch.onblur = function() {
document.getElementById('searchHousing').removeChild(document.getElementsByClassName("searchBox")[0]);
}
But as CVG mentions, creating/deleting elements live is a fairly bad idea.
The problem with onBlur as the other answers suggest is that you will then hide the search options div every time you click anything in it (since the input field will lose focus). This is probably not what you're looking for. Instead, you can add a click handler for the document to hide the search options, and then add a click handler for the search housing to prevent event propagation (which keeps the document click handler from hiding the options).
If you click the input box, the handler shows the options
If you click the input box or the options, the event propagation stops
If you click anywhere in the document besides the input box or options, the options are hidden
Updated JSFiddle
var inputSearch = document.getElementById('fillIn');
var searchHousing = document.getElementById('searchHousing')
inputSearch.onclick = function(event) {
document.getElementById('searchBox').style.display = 'block';
};
searchHousing.onclick = function(event) {
event.stopPropagation(); // won't be passed to document.onclick
};
document.onclick = function() {
document.getElementById('searchBox').style.display = 'none';
};
You'll be creating div elements all the time, which isn't any good. You could something along the lines of toggling the display attribute.
Just to make sure, if you want the searchBox to stay open and only close if you're clicking outside the searchBox and fillin elements, then you can just follow the click events and only toggle the display if you're clicking outside both. The other methods will just close it out as long as you're not typing in the input, and if you want to place tools or something within the div, then that would be useless.
document.addEventListener('click', function(e) {
if(e.target.id != "searchBox" && e.target.id != "fillIn")
document.getElementById("searchBox").style.display="none";
});
Full demonstration: JSFiddle
I am having trouble with the onmouseout function in an absolute positoned div. When the mouse hits a child element in the div, the mouseout event fires, but I do not want it to fire until the mouse is out of the parent, absolute div.
How can I prevent the mouseout event from firing when it hits a child element WITHOUT jquery.
I know this has something to do with event bubbling, but I am having no luck on finding out how to work this out.
I found a similar post here: How to disable mouseout events triggered by child elements?
However that solution uses jQuery.
Use onmouseleave.
Or, in jQuery, use mouseleave()
It is the exact thing you are looking for. Example:
<div class="outer" onmouseleave="yourFunction()">
<div class="inner">
</div>
</div>
or, in jQuery:
$(".outer").mouseleave(function(){
//your code here
});
an example is here.
For a simpler pure CSS solution that works in most cases, one could remove children's pointer-events by setting them to none
.parent * {
pointer-events: none;
}
Browser support: IE11+
function onMouseOut(event) {
//this is the original element the event handler was assigned to
var e = event.toElement || event.relatedTarget;
if (e.parentNode == this || e == this) {
return;
}
alert('MouseOut');
// handle mouse event here!
}
document.getElementById('parent').addEventListener('mouseout',onMouseOut,true);
I made a quick JsFiddle demo, with all the CSS and HTML needed, check it out...
EDIT FIXED link for cross-browser support http://jsfiddle.net/RH3tA/9/
NOTE that this only checks the immediate parent, if the parent div had nested children then you have to somehow traverse through the elements parents looking for the "Orginal element"
EDIT example for nested children
EDIT Fixed for hopefully cross-browser
function makeMouseOutFn(elem){
var list = traverseChildren(elem);
return function onMouseOut(event) {
var e = event.toElement || event.relatedTarget;
if (!!~list.indexOf(e)) {
return;
}
alert('MouseOut');
// handle mouse event here!
};
}
//using closure to cache all child elements
var parent = document.getElementById("parent");
parent.addEventListener('mouseout',makeMouseOutFn(parent),true);
//quick and dirty DFS children traversal,
function traverseChildren(elem){
var children = [];
var q = [];
q.push(elem);
while (q.length > 0) {
var elem = q.pop();
children.push(elem);
pushAll(elem.children);
}
function pushAll(elemArray){
for(var i=0; i < elemArray.length; i++) {
q.push(elemArray[i]);
}
}
return children;
}
And a new JSFiddle, EDIT updated link
instead of onmouseout use onmouseleave.
You haven't showed to us your specific code so I cannot show you on your specific example how to do it.
But it is very simple: just replace onmouseout with onmouseleave.
That's all :) So, simple :)
If not sure how to do it, see explanation on:
https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_onmousemove_leave_out
Peace of cake :) Enjoy it :)
Here's a more elegant solution based on what came below.
it accounts for event bubbling up from more than one level of children.
It also accounts for cross-browser issues.
function onMouseOut(this, event) {
//this is the original element the event handler was assigned to
var e = event.toElement || event.relatedTarget;
//check for all children levels (checking from bottom up)
while(e && e.parentNode && e.parentNode != window) {
if (e.parentNode == this|| e == this) {
if(e.preventDefault) e.preventDefault();
return false;
}
e = e.parentNode;
}
//Do something u need here
}
document.getElementById('parent').addEventListener('mouseout',onMouseOut,true);
Thanks to Amjad Masad that inspired me.
I've the following solution which seems to work in IE9, FF and Chrome and the code is quite short (without the complex closure and transverse child things) :
DIV.onmouseout=function(e){
// check and loop relatedTarget.parentNode
// ignore event triggered mouse overing any child element or leaving itself
var obj=e.relatedTarget;
while(obj!=null){
if(obj==this){
return;
}
obj=obj.parentNode;
}
// now perform the actual action you want to do only when mouse is leaving the DIV
}
If you're using jQuery you can also use the "mouseleave" function, which deals with all of this for you.
$('#thetargetdiv').mouseenter(do_something);
$('#thetargetdiv').mouseleave(do_something_else);
do_something will fire when the mouse enters thetargetdiv or any of its children, do_something_else will only fire when the mouse leaves thetargetdiv and any of its children.
I think Quirksmode has all the answers you need (different browsers bubbling behaviour and the mouseenter/mouseleave events), but I think the most common conclusion to that event bubbling mess is the use of a framework like JQuery or Mootools (which has the mouseenter and mouseleave events, which are exactly what you intuited would happen).
Have a look at how they do it, if you want, do it yourself
or you can create your custom "lean mean" version of Mootools with just the event part (and its dependencies).
Try mouseleave()
Example :
<div id="parent" mouseleave="function">
<div id="child">
</div>
</div>
;)
I've found a very simple solution,
just use the onmouseleave="myfunc()" event than the onmousout="myfunc()" event
In my code it worked!!
Example:
<html>
<head>
<script type="text/javascript">
function myFunc(){
document.getElementById('hide_div').style.display = 'none';
}
function ShowFunc(){
document.getElementById('hide_div').style.display = 'block';
}
</script>
</head>
<body>
<div onmouseleave="myFunc()" style='border:double;width:50%;height:50%;position:absolute;top:25%;left:25%;'>
Hover mouse here
<div id='child_div' style='border:solid;width:25%;height:25%;position:absolute;top:10%;left:10%;'>
CHILD <br/> It doesn't fires if you hover mouse over this child_div
</div>
</div>
<div id="hide_div" >TEXT</div>
Show "TEXT"
</body>
</html>
Same Example with mouseout function:
<html>
<head>
<script type="text/javascript">
function myFunc(){
document.getElementById('hide_div').style.display = 'none';
}
function ShowFunc(){
document.getElementById('hide_div').style.display = 'block';
}
</script>
</head>
<body>
<div onmouseout="myFunc()" style='border:double;width:50%;height:50%;position:absolute;top:25%;left:25%;'>
Hover mouse here
<div id='child_div' style='border:solid;width:25%;height:25%;position:absolute;top:10%;left:10%;'>
CHILD <br/> It fires if you hover mouse over this child_div
</div>
</div>
<div id="hide_div">TEXT</div>
Show "TEXT"
</body>
</html>
Hope it helps :)
Although the solution you referred to uses jquery,
mouseenter and mouseleave are native dom events, so you might use without jquery.
There are two ways to handle this.
1) Check the event.target result in your callback to see if it matches your parent div
var g_ParentDiv;
function OnMouseOut(event) {
if (event.target != g_ParentDiv) {
return;
}
// handle mouse event here!
};
window.onload = function() {
g_ParentDiv = document.getElementById("parentdiv");
g_ParentDiv.onmouseout = OnMouseOut;
};
<div id="parentdiv">
<img src="childimage.jpg" id="childimg" />
</div>
2) Or use event capturing and call event.stopPropagation in the callback function
var g_ParentDiv;
function OnMouseOut(event) {
event.stopPropagation(); // don't let the event recurse into children
// handle mouse event here!
};
window.onload = function() {
g_ParentDiv = document.getElementById("parentdiv");
g_ParentDiv.addEventListener("mouseout", OnMouseOut, true); // pass true to enable event capturing so parent gets event callback before children
};
<div id="parentdiv">
<img src="childimage.jpg" id="childimg" />
</div>
simply we can check e.relatedTarget has child class and if true return the function.
if ($(e.relatedTarget).hasClass("ctrl-btn")){
return;
}
this is code worked for me, i used for html5 video play,pause button toggle hover video element
element.on("mouseover mouseout", function(e) {
if(e.type === "mouseout"){
if ($(e.relatedTarget).hasClass("child-class")){
return;
}
}
});
I make it work like a charm with this:
function HideLayer(theEvent){
var MyDiv=document.getElementById('MyDiv');
if(MyDiv==(!theEvent?window.event:theEvent.target)){
MyDiv.style.display='none';
}
}
Ah, and MyDiv tag is like this:
<div id="MyDiv" onmouseout="JavaScript: HideLayer(event);">
<!-- Here whatever divs, inputs, links, images, anything you want... -->
<div>
This way, when onmouseout goes to a child, grand-child, etc... the style.display='none' is not executed; but when onmouseout goes out of MyDiv it runs.
So no need to stop propagation, use timers, etc...
Thanks for examples, i could make this code from them.
Hope this helps someone.
Also can be improved like this:
function HideLayer(theLayer,theEvent){
if(theLayer==(!theEvent?window.event:theEvent.target)){
theLayer.style.display='none';
}
}
And then the DIVs tags like this:
<div onmouseout="JavaScript: HideLayer(this,event);">
<!-- Here whatever divs, inputs, links, images, anything you want... -->
<div>
So more general, not only for one div and no need to add id="..." on each layer.
If you have access to the element which the event is attached to inside the mouseout method, you can use contains() to see if the event.relatedTarget is a child element or not.
As event.relatedTarget is the element to which the mouse has passed into, if it isn't a child element, you have moused out of the element.
div.onmouseout = function (event) {
if (!div.contains(event.relatedTarget)) {
// moused out of div
}
}
On Angular 5, 6 and 7
<div (mouseout)="onMouseOut($event)"
(mouseenter)="onMouseEnter($event)"></div>
Then on
import {Component,Renderer2} from '#angular/core';
...
#Component({
selector: 'app-test',
templateUrl: './test.component.html',
styleUrls: ['./test.component.scss']
})
export class TestComponent implements OnInit, OnDestroy {
...
public targetElement: HTMLElement;
constructor(private _renderer: Renderer2) {
}
ngOnInit(): void {
}
ngOnDestroy(): void {
//Maybe reset the targetElement
}
public onMouseEnter(event): void {
this.targetElement = event.target || event.srcElement;
console.log('Mouse Enter', this.targetElement);
}
public onMouseOut(event): void {
const elementRelated = event.toElement || event.relatedTarget;
if (this.targetElement.contains(elementRelated)) {
return;
}
console.log('Mouse Out');
}
}
I check the original element's offset to get the page coordinates of the element's bounds, then make sure the mouseout action is only triggered when the mouseout is out of those bounds. Dirty but it works.
$(el).live('mouseout', function(event){
while(checkPosition(this, event)){
console.log("mouseovering including children")
}
console.log("moused out of the whole")
})
var checkPosition = function(el, event){
var position = $(el).offset()
var height = $(el).height()
var width = $(el).width()
if (event.pageY > position.top
|| event.pageY < (position.top + height)
|| event.pageX > position.left
|| event.pageX < (position.left + width)){
return true
}
}
var elem = $('#some-id');
elem.mouseover(function () {
// Some code here
}).mouseout(function (event) {
var e = event.toElement || event.relatedTarget;
if (elem.has(e).length > 0) return;
// Some code here
});
If you added (or have) a CSS class or id to the parent element, then you can do something like this:
<div id="parent">
<div>
</div>
</div>
JavaScript:
document.getElementById("parent").onmouseout = function(e) {
e = e ? e : window.event //For IE
if(e.target.id == "parent") {
//Do your stuff
}
}
So stuff only gets executed when the event is on the parent div.
I just wanted to share something with you.
I got some hard time with ng-mouseenter and ng-mouseleave events.
The case study:
I created a floating navigation menu which is toggle when the cursor is over an icon.
This menu was on top of each page.
To handle show/hide on the menu, I toggle a class.
ng-class="{down: vm.isHover}"
To toggle vm.isHover, I use the ng mouse events.
ng-mouseenter="vm.isHover = true"
ng-mouseleave="vm.isHover = false"
For now, everything was fine and worked as expected.
The solution is clean and simple.
The incoming problem:
In a specific view, I have a list of elements.
I added an action panel when the cursor is over an element of the list.
I used the same code as above to handle the behavior.
The problem:
I figured out when my cursor is on the floating navigation menu and also on the top of an element, there is a conflict between each other.
The action panel showed up and the floating navigation was hide.
The thing is that even if the cursor is over the floating navigation menu, the list element ng-mouseenter is triggered.
It makes no sense to me, because I would expect an automatic break of the mouse propagation events.
I must say that I was disappointed and I spend some time to find out that problem.
First thoughts:
I tried to use these :
$event.stopPropagation()
$event.stopImmediatePropagation()
I combined a lot of ng pointer events (mousemove, mouveover, ...) but none help me.
CSS solution:
I found the solution with a simple css property that I use more and more:
pointer-events: none;
Basically, I use it like that (on my list elements):
ng-style="{'pointer-events': vm.isHover ? 'none' : ''}"
With this tricky one, the ng-mouse events will no longer be triggered and my floating navigation menu will no longer close himself when the cursor is over it and over an element from the list.
To go further:
As you may expect, this solution works but I don't like it.
We do not control our events and it is bad.
Plus, you must have an access to the vm.isHover scope to achieve that and it may not be possible or possible but dirty in some way or another.
I could make a fiddle if someone want to look.
Nevertheless, I don't have another solution...
It's a long story and I can't give you a potato so please forgive me my friend.
Anyway, pointer-events: none is life, so remember it.
There are a simple way to make it work. The element and all childs you set a same class name, then:
element.onmouseover = function(event){
if (event.target.className == "name"){
/*code*/
}
}
Also for vanillajs you can use that way
document.querySelector('.product_items') && document.querySelector('.product_items').addEventListener('mouseleave', () => updateCart())
const updateCart = () => {
let total = 0;
document.querySelectorAll('input') && document.querySelectorAll('input').forEach(item => total += +item.value)
document.getElementById('total').innerHTML = total
}
<div class="product_items">
<div class="product_item">
<div class="product_name">
</div>
<div class="multiply__btn">
<button type="button">-</button>
<input name="test" type="text">
<button type="button">+</button>
</div>
</div>
<div class="product_item">
<div class="product_name">
</div>
<div class="multiply__btn">
<button type="button">-</button>
<input name="test" type="text">
<button type="button">+</button>
</div>
</div>
<div class="product_item">
<div class="product_name">
</div>
<div class="multiply__btn">
<button type="button">-</button>
<input name="test" type="text">
<button type="button">+</button>
</div>
</div>
</div>
<div id="total"></div>
If for some reason you don't want to use the mouseenter and mouseleave events, you can use mouseover/mouseout with a little "debouncing".
The idea relies on the fact that your event handler will receive out followed by a new over when crossing boundaries between various child elements....except when the mouse has really left (for longer than the debounce period). This seems simpler than crawling the dom nodes on every event.
If you "debounce" with a short delay before assuming you have a real out you can effectively ignore all these out/over events bubbling up from child elements.
Note! This will not work if a child element also has a listener for over and/or out events AND their handler calls event.stopPropogation() to stop the event from bubbling up to the parent element where we have attached our handler. If you control the code, this is not necessarily a problem, but you should be aware.
sample code
javascript
function mouseOverOutDebounce (element, debounceMs, mouseOverFn, mouseOutFn) {
var over = false,
debounceTimers = [];
function mouseOver (evt) {
if (over) { // already OVER, existing interaction
while (debounceTimers.length > 0) { // then we had a pending mouseout(s), cancel
window.clearTimeout(debounceTimers.shift());
}
}
else { // new OVER
over = true;
mouseOverFn(evt);
}
}
function mouseOut (evt) {
if (!over) return; // already OUT, ignore.
debounceTimers.push(window.setTimeout(function () {
over = false;
mouseOutFn(evt);
}, debounceMs));
}
function removeEventListeners () {
element.removeEventListener('mouseover', mouseOver);
element.removeEventListener('mouseout', mouseOut);
}
element.addEventListener('mouseover', mouseOver);
element.addEventListener('mouseout', mouseOut);
return removeEventListeners;
}
var someEl = document.querySelector('.container'),
textarea = document.querySelector('textarea'),
mouseOver = function (evt) { report('mouseOVER', evt); },
mouseOut = function (evt) { report('mouseOUT', evt); },
removeEventListeners = mouseOverOutDebounce(someEl, 200, mouseOver, mouseOut);
function report(msg, data) {
console.log(msg, data);
textarea.value = textarea.value + msg + '\n';
}
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title></title>
<style type="text/css">
html, body {
margin: 0;
padding: 0;
}
body {
margin: 5%;
}
.container {
width: 300px;
height: 600px;
border: 10px solid red;
background-color: #dedede;
float: left;
}
.container .square {
width: 100px;
height: 100px;
background-color: #2086cf;
margin: -10px 0 0 -10px;
}
textarea {
margin-left: 50px;
width: 800px;
height: 400px;
background-color: #464646;
font-family: monospace;
color: white;
}
.bar {
width: 2px;
height: 30px;
display: inline-block;
margin-left: 2px;
background-color: pink;
}
</style>
</head>
<body>
<div class="container">
<div class="square"></div>
<div class="bar"></div>
<div class="bar"></div>
<div class="bar"></div>
<div class="bar"></div>
<div class="bar"></div>
<div class="bar"></div>
<div class="bar"></div>
</div>
<textarea></textarea>
<script src="interactions.js"></script>
</body>
</html>
fiddle
https://jsfiddle.net/matp/9bhjkLct/5/