How Can Conflict Between Mouse Events Be Resolved - javascript

I have a div called starter-box that when clicked is supposed to dynamically create 2 divs. I added an event listener to the parent of the starter-box div to listen for any event of the 2 dynamically created divs.
The problem is that the click event listener conflicts with the mousedown event listener preventing it (the click event) from executing.
document.addEventListener('DOMContentLoaded', function () {
var container = document.querySelector("#container");
var target;
var elementCreated = false;
var elements = [];
container.addEventListener("click", (ev) => {
target = ev.target;
if (target.className != "box" && target.id != "container") {
if (elementCreated == false) {
createElements(target);
} else if (elementCreated == true) {
removeElementsByClass("box");
elements = [];
}
}
});
container.addEventListener("mousedown", (ev) => {
target = ev.target;
alert("MouseDown on: " + target.id);
});
container.addEventListener("mouseup", (ev) => {
target = ev.target;
alert("MouseUp on: " + target.id);
});
function createElements(target) {
const dimensions = target.getBoundingClientRect();
const element1 = document.createElement('div');
element1.className = "box";
element1.id = "box1";
element1.style.left = (dimensions.left) + "px";
element1.style.top = dimensions.bottom + 25 + "px";
container.appendChild(element1);
const element2 = document.createElement('div');
element2.className = "box";
element2.id = "box2";
element2.style.left = (dimensions.left) + "px";
element2.style.top = dimensions.bottom + 90 + "px";
container.appendChild(element2);
}
});
#container {
width: 600px;
height: 400px;
background-color: blue
}
#starter-box {
position: relative;
top: 50px;
left: 25px;
width: 50px;
height: 50px;
background-color: red
}
.box {
position: absolute;
width: 50px;
height: 50px;
background-color: black
}
<div id="container">
<div id="starter-box">
</div>
</div>

Check the fields of event object (ev argument to the listener callback).
In case of 'click' event: ev.type === 'click'
In case of 'mousedown' event: ev.type === 'mousedown'
You can use it your logic.
Discover the event object, try to console.log(ev), and see. There are more differences.

Related

How to detect when a mouse moves away from the element

mousemove is fired when mouse is moving over an element. How can I detect when the mouse is moving outside of an element? In other words, anywhere on the page besides the div in the snippet. Not when the mouse leaves but fires whenever the mouse is moving outside of the element.
const div = document.querySelector('div');
div.addEventListener('mousemove', function() {
document.body.classList.add('mouse-moving');
});
div {
height: 200px;
width: 300px;
background-color: red;
}
.mouse-moving {
background-color: green;
}
<div></div>
You can use onmouseover and onmouseout
const div = document.querySelector('div');
div.onmouseover = ()=> document.body.classList.add('mouse-moving');
div.onmouseout = ()=> document.body.classList.remove('mouse-moving');
div {
height: 200px;
width: 300px;
background-color: red;
}
.mouse-moving {
background-color: green;
}
<div></div>
You can add the mousemove event listener to the document and check whether the event target is your div or not:
const div = document.querySelector('div');
document.addEventListener('mousemove', function(e) {
if(e.target !== div) {
div.textContent = "outside the div (" + e.clientX + ", " + e.clientY + ")";
} else {
div.textContent = "inside the div (" + e.clientX + ", " + e.clientY + ")";
}
});
div {
height: 200px;
width: 300px;
background-color: red;
}
<div></div>
Note: If the div contain other element, the test won't work. You'll have to check if one of the target's ancestors is your div:
document.addEventListener('mousemove', function(e) {
var elm;
for(elm = e.target; elm && elm !== div; elm = elm.parentElement)
;
if(elm === div) {
// inside div
} else {
// outside div
}
});
const div = document.querySelector('div'),
result = document.querySelector("#result");
document.addEventListener('mousemove', function(e) {
var elm;
for(elm = e.target; elm && elm !== div; elm = elm.parentElement)
;
if(elm === div) {
result.textContent = "inside the div (" + e.clientX + ", " + e.clientY + ")";
} else {
result.textContent = "outside the div (" + e.clientX + ", " + e.clientY + ")";
}
});
div {
height: 200px;
width: 300px;
background-color: red;
}
div > div {
height: 50px;
width: 50px;
background-color: blue;
margin: 5px;
}
<span id="result"></span>
<div>
<div></div>
<div></div>
</div>
Also, if the div's children are outside its boundary (due to some absolute positioning or something), the above method won't work, you'll have to check if the mouse coordinates are inside the div's bounding rectangle.

My hover popup is flickering and moving when it shouldn't

Trying to create a popup that will show when hovering over an element. However it flickers and moves around when I move my mouse inside the element. It should also stay open if the mouse moves over the popup.
Trying to do this without library cheats like jQuery. You don't learn if you use them.
If you hover your mouse over one of the tags below, that's exactly what I'm trying to create.
Think the error is somewhere in this code:
function showPopup(e) {
var popup = document.getElementById('popup');
if (popup.style.display == 'none') {
popup.style.display = 'block';
var bodyRect = document.body.getBoundingClientRect(),
elemRect = e.target.getBoundingClientRect(),
offsetX = elemRect.left - bodyRect.left,
offsetY = elemRect.bottom - bodyRect.top;
popup.style.left = offsetX + 'px';
popup.style.top = offsetY + 'px';
//console.log(e);
}
}
function hidePopup(/*e*/) {
setTimeout(function() {
var popup = document.getElementById('popup');
if (popup.style.display == 'block' && !window.inside_popup) {
popup.style.display = 'none';
window.inside_popup = false;
console.log('hide');
} else {
setTimeout(hidePopup, 50); // try a little later
}
}, 50); // Give the events ability to catch up and tell us the mouse is inside the popup
}
var targ = document.querySelector('ul li')
targ.addEventListener('mouseover', showPopup);
targ.addEventListener('mouseout', hidePopup);
Full javascript code with a real test element:
https://jsfiddle.net/g8wvae8o/
As #epascarello said, mouseleave and mouseenter are what you're looking for. There's no need for setTimeout here either. In addition, you're targeting every li on the page (is that intentional?) I recommend targeting a specific class of element to reduce flickering.
This is close, but you'll need to massage the positioning.
function createPopup() {
var container = document.createElement('div');
container.id = 'popup';
container.style.width = '500px';
container.style.height = '700px';
container.style.display = 'none';
container.style.position = 'absolute';
container.style.borderRadius = '2px';
container.style.border = '1px solid #242729';
container.style.backgroundColor = '#535a60';
container.style.color = '#e4e6e8';
container.style.zIndex = '9999999';
container.addEventListener('xmouseenter', function() {
window.inside_popup = true;
//console.log('window.inside_popup = true;');
});
container.addEventListener('xmouseleave', function() {
window.inside_popup = false;
//console.log('window.inside_popup = false;');
});
container.appendChild(document.createTextNode('This is a test'));
(document.body || document.documentElement).appendChild(container);
}
window.inside_popup = false;
createPopup();
function showPopup(e) {
var popup = document.getElementById('popup');
if (popup.style.display == 'none') {
popup.style.display = 'block';
}
}
function hidePopup(/*e*/) {
console.log('hiding')
popup.style.display = 'none';
window.inside_popup = false;
}
var bodyRect = document.body.getBoundingClientRect()
function updatePopup(e) {
var elemRect = e.target.getBoundingClientRect(),
offsetY = elemRect.bottom - bodyRect.top,
offsetX = elemRect.left - bodyRect.left;
popup.style.left = (e.clientX + offsetX) + 'px';
popup.style.top = offsetY + 'px';
}
var targ = document.querySelector('ul li')
targ.addEventListener('mouseenter', showPopup);
targ.addEventListener('mouseleave', hidePopup);
targ.addEventListener('mousemove', updatePopup)
Fiddle
Here's a pure CSS solution (I only use JS to create the popup elements)
window.addEventListener("load", function () {
var els = document.querySelectorAll("li");
els.forEach(el => {
var popup = document.createElement("div");
popup.innerHTML = el.getAttribute("popup");
popup.className = "popup";
el.appendChild(popup);
});
});
*[popup]:hover > .popup {
border: 1px solid #fff;
padding: 0.5em;
width: 400px;
height: auto
}
.popup {
overflow: hidden;
box-sizing: border-box;
background-color: black;
color: #ccc;
border-radius: 3px;
position: absolute;
height: 0px;
}
li {
margin: 2em 0
}
<ul>
<li popup="Some more info about this product">Move the mouse here</li>
<li popup="Some more info about the 2nd product">Some other product</li>
</ul>
The key to this is that the popup is a child of the element that is hovered, thus moving the mouse over the popup still counts as hovering the element.

Div placement in javascript

I would like it to create the div where the mouse is. I have the following code:
var mouseisdown = false;
$(document).mousedown(function(event) {
mouseisdown = true;
doSomething();
}).mouseup(function(event) {
mouseisdown = false;
});
function doSomething(e){
var draw = document.createElement("div");
draw.className = "draw";
document.body.appendChild(draw);
draw.style.top = e.clientY + "px";
draw.style.left = e.clientX + "px";
if (mouseisdown)
doSomething();
}
Basically you already had it, but you overcomplicated it:
Remove the mouseisdown variable and the event listeners
Add doSomething as a click event listener
Don't call doSomething recursively
$(document).click(function doSomething(e){
var draw = document.createElement("div");
draw.className = "draw";
document.body.appendChild(draw);
draw.style.top = e.clientY + "px";
draw.style.left = e.clientX + "px";
});
.draw {
position: absolute;
height: 10px;
width: 10px;
margin: -5px;
background: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Click somewhere

Listening for addEventListener calls for a custom event

I'm trying to build a custom drag event. Here's my initial pseudocode, note that I left event cleaning up out.
var dragEvent = new CustomEvent("drag");
var anArbitrairyElement = document.querySelector(".box");
anArbitrairyElement.addEventListener("drag", function () {
console.log("event received");
});
(function () {
var dragEventListeners = [];
window.addEventListener("mousedown", function (mousedownEvent) {
dragEventListeners.forEach(function (target) {
if (mousedownEvent.target === target) {
window.addEventListener("mousemove", function (mousemoveEvent) {
target.dispatchEvent(dragEvent);
});
}
// ...
});
});
// does something like this exist?
onDragEventAdded(function (listenerElement) {
dragEventListeners.push(listenerElement);
});
}());
Is there any way I can listen to addEventListener calls without overwriting the addEventListener function itself? The solution needs to end-up in this being possible:
document.querySelector(".someElement").addEventListener("drag", ...);
Otherwise, is there another way how I could achieve the desired behavior of creating a custom drag event?
Do it the following way.
window.addEventListener("mousedown", function (mousedownEvent) {
var mouseMoveHandler =
function(element) {
return function(mouseMoveEvent) {
element.dispatchEvent(dragEvent);
}
}(mousedownEvent.target);
window.addEventListener("mousemove", mouseMoveHandler);
});
});
So in this case on mousedown event you create closure that will trigger drag event on the clicked element. You do not need the array of elements that were clicked. Clicked element is already injected to the handler.
Don't forget to clean listeners on mouseup. Just drop the mousemove listener on window
For anyone who wants to use the drag event, here's an example utilizing it.
var box1 = document.createElement("div");
var box2 = document.createElement("div");
var box3 = document.createElement("div");
document.body.appendChild(box1);
document.body.appendChild(box2);
document.body.appendChild(box3);
box1.className = "box";
box2.className = "box";
box3.className = "box";
box1.innerHTML = "Drag me";
box2.innerHTML = "No drag";
box3.innerHTML = "Drag me";
function dragElement(event) {
event.target.style.top = parseInt(event.target.style.top.split("px")[0] || 0) + event.dy + "px";
event.target.style.left = parseInt(event.target.style.left.split("px")[0] || 0) + event.dx + "px";
}
box1.addEventListener("drag", dragElement);
box3.addEventListener("drag", dragElement);
// custom event logic starting here
var dragEvent = new CustomEvent("drag");
window.addEventListener("mousedown", function (mousedownEvent) {
var mousePosition = {x: mousedownEvent.clientX, y: mousedownEvent.clientY};
(function () {
var target = mousedownEvent.target;
console.log(target);
function moveHandler(event) {
var newMousePosition = {x: event.clientX, y: event.clientY};
dragEvent.dx = newMousePosition.x - mousePosition.x;
dragEvent.dy = newMousePosition.y - mousePosition.y;
dragEvent.clientX = event.clientX;
dragEvent.clientY = event.clientY;
target.dispatchEvent(dragEvent);
mousePosition = newMousePosition;
}
function releaseHandler() {
window.removeEventListener("mousemove", moveHandler);
window.removeEventListener("mouseup", releaseHandler);
}
window.addEventListener("mousemove", moveHandler);
window.addEventListener("mouseup", releaseHandler);
}());
});
.box {
width: 75px;
height: 75px;
background-color: skyblue;
display: inline-block;
margin: 10px;
position: relative;
text-align: center;
font-family: helvetica;
color: white;
vertical-align: middle;
line-height: 75px;
font-weight: bold;
}

When I want to make a draggable div I couldn't avoid selecting neighboured text

I am a JS beginner.
I have made a draggable <div>. When I drag it, it will select one or two words near the <div> or glint with the blue select box (see the code-snippet below).
It bothers the experience feeling a little. What I want most is to keep other normal words being selected when dragging dhe <div>.
Please help me optimize the code or tell me if there is any syntax mistake.
window.onload = function() {
var parent = document.getElementById('parent');
eventFunc.addEventListener(parent, 'mousedown', dragItem);
}
function dragItem(ev) {
ev = ev || window.event;
var element = eventFunc.target(ev);
var spaceX = ev.clientX - element.offsetLeft,
spaceY = ev.clientY - element.offsetTop,
maxX = (document.documentElement.clientWidth || document.body.clientWidth) - element.offsetWidth,
maxY = (document.documentElement.clientHeight || document.body.clientHeight) - element.offsetHeight;
document.onmousemove = function(ev) {
ev = ev || window.event;
eventFunc.stopPropagation(ev);
element.style.left = (ev.clientX - spaceX) + 'px';
element.style.top = (ev.clientY - spaceY) + 'px';
if (element.offsetLeft < 0) {
element.style.left = 0;
} else if (element.offsetLeft > maxX) {
element.style.left = maxX + 'px'
}
if (element.offsetTop < 0) {
element.style.top = 0;
} else if (element.offsetTop > maxY) {
element.style.top = maxY + 'px'
}
}
document.onmouseup = function() {
document.onmousemove = null;
document.onmouseup = null;
}
}
var eventFunc = {
addEventListener: function(element, type, func) {
if (addEventListener) {
element.addEventListener(type, func, false);
} else if (attachEvent) {
element.attachEvent('on' + type, func);
} else {
element['on' + type] = func;
}
},
target: function(ev) {
return ev.target || ev.srcElement;
},
stopPropagation: function(ev) {
if (ev.stopPropagation) {
ev.stopPropagation();
} else {
ev.cancelBubble = true;
}
},
preventDefault: function(ev) {
if (ev.preventDefault) {
ev.preventDefault();
} else {
ev.returnValue = false;
}
}
}
#parent {
width: 400px;
height: 300px;
position: absolute;
border: 3px solid #ccc;
top: 0;
left: 0;
cursor: all-scroll;
text-align: center;
font: bold 35px/35px'Segoe UI', arial, helvetica, sans-serif;
color: #ccc;
}
#children {
text-align: left;
color: initial;
font: initial;
cursor: auto;
margin: 1em;
border: 1px solid red;
}
Outside Words
<div id="parent">
<div id="children">I'm The Children Element. I'm The Children Element. I'm The Children Element. I'm The Children Element. I'm The Children Element. I'm The Children Element. I'm The Children Element. I'm The Children Element. I'm The Children Element.</div>
DRAG ME
</div>
</body>
inside your dragItem function.... add the following line...
function dragItem(ev) {
ev.preventDefault()
}
[edit (fulfilling the rest of the question)]
To select contents of any piece of content... you can use this function...
function selectNode(el) {
var range = document.createRange()
range.selectNodeContents(el)
var sel = window.getSelection()
sel.removeAllRanges()
sel.addRange(range)
}
// use it by passing in a node
selectNode(document.getElementById('parent'))

Categories