:hover select target only, not parents with same class - javascript

How do I make this only fire :hover for the target element (ignoring the parents)?
Assume this is recursive design of object inside object, therefore with the same classes and an autogenerated id.
.group:hover {
background-color: red;
}
.group {
border: 1px solid black;
width: 100px;
padding: 20px;
}
<div id="g1" class="group">aaaa
<div id="g2" class="group">bbbb
<div id="g3" class="group">cccc
</div>
</div>
</div>

Since you tagged the question with javascript you can achieve this using it. The key is to use .stopProgagation() which will stop events from "falling through" down to your other elements.
See example below:
document.querySelectorAll(".group").forEach(elem => {
elem.addEventListener('mouseover', function(e) {
e.stopPropagation();
this.classList.add('group-hover');
});
elem.addEventListener('mouseout', function(e) {
this.classList.remove('group-hover');
});
});
.group-hover {
background-color: red;
}
.group {
border: 1px solid black;
width: 100px;
padding: 20px;
}
<div id="g1" class="group">aaaa
<div id="g2" class="group">bbbb
<div id="g3" class="group">cccc
</div>
</div>
</div>
Alternatively, you could intead use e.target to get the target of the event if you wish not to use stopPropagation():
document.querySelectorAll(".group").forEach(elem => {
elem.addEventListener('mouseover', e => e.target.classList.add('group-hover'));
elem.addEventListener('mouseout', e => e.target.classList.remove('group-hover'));
});
.group-hover {
background-color: red;
}
.group {
border: 1px solid black;
width: 100px;
padding: 20px;
}
<div id="g1" class="group">aaaa
<div id="g2" class="group">bbbb
<div id="g3" class="group">cccc
</div>
</div>
</div>

Only with JS, and using events delegate for simpler way
const All_g = document.querySelector('#g1');
All_g.onmouseover = function(event) {
let target = event.target;
target.style.background = 'red';
};
All_g.onmouseout = function(event) {
let target = event.target;
target.style.background = '';
};
.group {
border: 1px solid black;
width: 100px;
padding: 20px;
}
<div id="g1" class="group">aaaa
<div id="g2" class="group">bbbb
<div id="g3" class="group">cccc
</div>
</div>
</div>
some explanations :=> https://javascript.info/mousemove-mouseover-mouseout-mouseenter-mouseleave

You can do it in the following way:
var elements = document.getElementsByClassName('group');
var lastElement = null;
elements.each(element =>{
lastElement = element;
});
lastElement.on('hover', function(){
//do anything you wish with element
})

Approach 1
Register hover event to toggle class and use event.stopPropagation();
https://developer.mozilla.org/en-US/docs/Web/API/Event/stopPropagation
The bubbles read-only property of the Event interface indicates
whether the event bubbles up through the DOM or not.
https://developer.mozilla.org/en-US/docs/Web/API/Event/bubbles
Approach 2
Mouseenter event - By design it does not bubble - so don't have to perform event.stopPropagation()
Though similar to mouseover, it differs in that it doesn't bubble and
that it isn't sent to any descendants when the pointer is moved from
one of its descendants' physical space to its own physical space.
https://developer.mozilla.org/en-US/docs/Web/API/Element/mouseenter_event

Related

can a click event be triggered on absolutely stacked elements?

I have a click event thats firing. It's working great and does what I need it to do. Here's the problem
The nature of the widget i'm building stacks elements on top of each other through position: absolute When i click on one of these stacked elements, only one event is firing, but id like every element to fire that is under the mouse cursor of the click. Is there a way to do this?
Please check the demo or run the code snippet in full page and click through all the divs to see the result message.
DEMO:
http://plnkr.co/edit/KRWvLmRhGbO200pFkOxL?p=preview
What I am doing here is :
Hide the top element
and
get the next absolute element's co-ordinate with document.elementFromPoint and then repeat.
Stack Snippet:
jQuery(document).ready(function(){
$common = $("div.common").on('click.passThrough', function (e, ee) {
var $element = $(this).hide();
try {
if (!ee) $("#output").empty();
$("<div/>").append('You have clicked on: '+$element.text()).appendTo($("#output"));
ee = ee || {
pageX: e.pageX,
pageY: e.pageY
};
var next = document.elementFromPoint(ee.pageX, ee.pageY);
next = (next.nodeType == 3) ? next.parentNode : next //Opera
$(next).trigger('click.passThrough', ee);
} catch (err) {
console.log("click.passThrough failed: " + err.message);
} finally {
$element.show();
}
});
$common.css({'backgroundColor':'rgba(0,0,0,0.2)'});
});
#output {
margin-bottom: 30px;
}
.common {
position: absolute;
height: 300px;
width: 300px;
padding: 3px;
border: 1px #000 solid;
}
.elem5 {
top: 150px;
left: 150px;
}
.elem4 {
top: 180px;
left: 180px;
}
.elem3 {
top: 210px;
left: 210px;
}
.elem2 {
top: 240px;
left: 240px;
}
.elem1 {
top: 270px;
left: 270px;
}
<script data-require="jquery#3.0.0" data-semver="3.0.0" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0/jquery.js"></script>
<div id="output"></div>
<div class="common elem1">Top Most Element</div>
<div class="common elem2">Element 2</div>
<div class="common elem3">Element 3</div>
<div class="common elem4">Element 4</div>
<div class="common elem5">Bottom Element</div>
Credit for source:
http://jsfiddle.net/E9zTs/2/
You can use customEvent property
Place all div in a parent div
add a click handler to the parent div
if there is a click in the parent box..determine whether the click is in any of the child boxes
If true. then send a click event to all child box
snippet
//This function changes the color of all child divs
function changeColor(e) {
this.style.background = "red";
}
//this function is attached to the parent div which will send that click event to all divs
function trigger(e) {
//create an event
event = new CustomEvent('click');
//if the event originates from a child div
if (e.target.className == 'box')
//loop through all child div
for (var i = 0; i < all_box.length; ++i) {
//dispatch a click event to each child div
all_box[i].dispatchEvent(event);
}
}
document.getElementById('parent').addEventListener('click', trigger)
var all_box = document.getElementsByClassName('box');
for (var i = 0; i < all_box.length; ++i) {
all_box[i].addEventListener('click', changeColor)
}
.box {
padding: 10px;
display: inline-block;
border: solid black;
}
#parent {
border: solid black;
padding: 10px;
}
;
<div id="parent">
<div class="box" id="primary">box1</div>
<div class="box">box2</div>
<div class="box">box3</div>
<div class="box">box3</div>
</div>

Removing certain elements from a selector binded to the body

I have a click event binded to the body element but I don't want it to fire for when the user clicks on certain elements, that being when the element has an attribute of data-dropdown-target, however what I have tried isn't working, it always fires.
CodePen: http://codepen.io/anon/pen/ORQkrb
HTML:
<body>
<div class="foo">foo</div>
<div class="bar" data-dropdown-target="something">bar</div>
<div class="moo">moo</div>
</body>
CSS:
.foo, .bar, .moo {
padding: 10px;
margin: 5px;
}
.foo {
background-color: gray;
}
.bar {
background-color: teal;
}
.moo {
background-color: green;
}
JS:
$('body').not('[data-dropdown-target]').on('click', function(e) {
console.log('Hi!');
});
I assume this is because it is trying to remove body elements that have this attribute, rather than it's children - correct?
How do I go about stopping it from firing on children elements that have this attribute - do I have to loop through everything, as I would like to avoid that because of performance reasons, especially since it's on the body.
Actually your code try to bind event click on every <body> without data-dropdown-target attribute.
This could solve your problem :
$('body').on('click', function(e) {
if($(e.target).data('dropdown-target') || $(e.target).parents('[data-dropdown-target]').length !== 0) return false;
console.log('Hi!');
});
.foo, .bar, .moo {
padding: 10px;
margin: 5px;
}
.foo {
background-color: gray;
}
.bar {
background-color: teal;
}
.moo {
background-color: green;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body>
<div class="foo">foo</div>
<div class="bar" data-dropdown-target="something">bar</div>
<div data-dropdown-target="something">
<div class="moo">moo</div>
</div>
</body>
The not selector just remove the body element if it has [data-dropdown-target] attribute.
Remove elements from the set of matched elements.
$('body').on('click', function(e) {
console.log('Hi!');
});
$('[data-dropdown-target]').on('click',function(e){
return false;
});

Using ESC event to cancel Drag and Drop

I am looking for a way to allow a user to cancel a mouse drag operation by pressing the ESC key.
Can this be done using Javascript?
Thank you
Update
When the mouse is dragging a div element over a droppable area, pressing the ESC key should drag the element to an area that is not droppable. Once the element is dragged to a non-droppable area, I invoke a "mouseup" event on the dragged element, which causes the dragged element to be dropped onto a non-droppable area.
How can I do this using jQuery Draggable and jQuery Droppable?
When the mouse is dragging a div element over a droppable area, pressing the ESC key should drag the element to an area that is not droppable
I´ve created a demo of a possible solution that you can check in plunker.
As stated by #ioneyed, you can select the dragged element directly using the selector .ui-draggable-dragging, which should be more efficient if you have lots of draggable elements.
The code used is the following, however, apparently it's not working in the snippet section. Use the fullscreen feature on the plunker or reproduce it locally.
var CANCELLED_CLASS = 'cancelled';
$(function() {
$(".draggable").draggable({
revert: function() {
// if element has the flag, remove the flag and revert the drop
if (this.hasClass(CANCELLED_CLASS)) {
this.removeClass(CANCELLED_CLASS);
return true;
}
return false;
}
});
$("#droppable").droppable();
});
function cancelDrag(e) {
if (e.keyCode != 27) return; // ESC = 27
$('.draggable') // get all draggable elements
.filter('.ui-draggable-dragging') // filter to remove the ones not being dragged
.addClass(CANCELLED_CLASS) // flag the element for a revert
.trigger('mouseup'); // trigger the mouseup to emulate the drop & force the revert
}
$(document).on('keyup', cancelDrag);
.draggable {
padding: 10px;
margin: 10px;
display: inline-block;
}
#droppable {
padding: 25px;
margin: 10px;
display: inline-block;
}
<div id="droppable" class="ui-widget-header">
<p>droppable</p>
</div>
<div class="ui-widget-content draggable">
<p>draggable</p>
</div>
<div class="ui-widget-content draggable">
<p>draggable</p>
</div>
<div class="ui-widget-content draggable">
<p>draggable</p>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.css">
I tried to help but without the expected result...
Searching on google you can find that while dragging other events are locked, similar behaviour to what happens during a window.alert...
By the way, I am on a Mac and I can capture all keyboard events but not "controls key such as command, ctrl, esc, ecc."
Hope help you as a starter point!
function DragAndDropCtrl($) {
var self = this;
self.ESC = 27;
self.draggables = $('.draggable');
self.dropArea = $('#droppable');
self.currentDraggingElement = null;
self.currentDismissed = false;
self.dismissDragging = function(event, eventManager) {
self.currentDismissed = true;
//Using the manager you can't use the revert function OMG!
//return eventManager.cancel();
};
self.dropArea.droppable();
self.draggables.draggable({
revert: function() {
var revert = self.currentDismissed;
self.currentDismissed = false;
console.log(revert, self.currentDismissed)
return revert;
},
start: function() {
self.currentDraggingElement = $(this);
},
end: function() {
self.currentDraggingElement = null;
}
});
$(document).keypress(function(event) {
console.log('key pressed', event)
//How to intercept the esc keypress?
self.dismissDragging(event, $.ui.ddmanager.current);
if(event.which === self.ESC || event.keyCode === self.ESC) {
self.dismissDragging(event, $.ui.ddmanager.current);
}
});
}
jQuery(document).ready(DragAndDropCtrl);
#droppable {
border: 1px solid #ddd;
background: lightseagreen;
text-align: center;
line-height: 200px;
margin: 1em .3em;
}
.draggable {
border: 1px solid #ddd;
display: inline-block;
width: 100%;
margin: .5em 0;
padding: 1em 2em;
cursor: move;
}
.sidebar { width: 30%; float: left; }
.main { width: 70%; float: right; }
* { box-sizing: border-box; }
<div class="sidebar">
<div class="ui-widget-content draggable">
<p>draggable</p>
</div>
<div class="ui-widget-content draggable">
<p>draggable</p>
</div>
<div class="ui-widget-content draggable">
<p>draggable</p>
</div>
</div>
<div class="main">
<div id="droppable" class="ui-widget-header">
<p>droppable</p>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.css">

Ionic Event-LIsteners on-hold/on-tab Parent/Child Issue

In an cordova/ionic app there is a parent-div on which on-hold-listener is attached and a child-div which subscribed for on-tab events like so:
<div on-hold="doSomething()">
<div on-tap="doSomething2()">...</div>
</div>
From time to time it works but there have been situations in what on-tab was executed instead of on-hold when pressing time was bigger than 500ms.
Might this be done in a better way? Please take into consideration that child-div fills out parent completely and it should remain so.
Thanks in advance.
When you have a div parent of another div the events are propagated.
You can try by yourself here:
.c2 {
width: 200px;
height: 200px;
background-color: red;
}
.c1 {
width: 220px;
height: 220px;
background-color: yellow;
}
<div class="c1" onclick="alert('squareParent');">
<div class="c2" onclick="alert('squareChild');">
</div>
</div>
To avoid this you need to stop the propagation:
document.getElementById("c1").addEventListener('click', function(e) {
alert("c1");
}, false);
document.getElementById("c2").addEventListener('click', function(e) {
alert("c2");
e.stopPropagation();
}, false);
#c2 {
width: 200px;
height: 200px;
background-color: red;
}
#c1 {
width: 220px;
height: 220px;
background-color: yellow;
}
<div id="c1">
<div id="c2">
</div>
</div>
You could check more about javascript bubble if you want more information.
After experimenting with stopPropagation I came up with following answer that needs setTimeout to check for mouse/cursor is being holded.
When just clicking on the child-div(red) doSomething2 is alerted whereas holding onto child-div alerts doSomething of parent instead:
var holding = false;
var secondFunctionCall = false;
document.getElementById("c1").addEventListener('mousedown', function(e) {
holding = true;
setTimeout(function(){
if(holding){
secondFunctionCall = false;
alert("calling doSomething()");
}
},500);
}, false);
document.getElementById("c2").addEventListener('mousedown', function(e) {
holding = true;
secondFunctionCall = true;
}, false);
document.getElementById("c2").addEventListener('mouseup', function(e) {
holding = false;
if(secondFunctionCall){
alert("calling doSomething2()");
}
}, false);
#c2 {
width: 200px;
height: 200px;
background-color: red;
}
#c1 {
width: 220px;
height: 220px;
background-color: yellow;
}
<div id="c1">
<div id="c2">
</div>
</div>
When transfering this code into a cordova-app mouse-event types have to be replaced by touch-event types as it is answered here.

z-index and onclick in HTML

I have the following HTML code:
<script src="http://code.jquery.com/jquery-1.6.2.min.js"></script>
<div id="A" style="width:100px; height: 100px; background: #00FF00; padding: 15px;
z-index: 50; opacity: .5" onclick="javascript:alert('A')">
<div id="B" style="width:50px; height: 50px; background: #FF0000; z-index:10;"
onclick="javascript:alert('B')" >
</div>
</div>
I was hoping this would make it so that clicking on div B's position would not invoke it's onclick, but only A's since A ha a higher z-index.
If not with z-index, how can I achieve this ?
You can use event delegation for that - no need for z-indexes and the like. Assing one (1) click handler to the topmost div and, within the handler, use the event target/srcElement to decide what (not) to do with the originating element. Something like:
<div id="A" style="width:100px; height: 100px;
background: #00FF00; padding: 15px;
z-index: 50; opacity: .5"">
<div id="B" style="width:50px; height: 50px;
background: #FF0000; z-index:10;" ></div>
</div>
The handler function:
function myHandler(e){
e = e || event;
var el = e.srcElement || e.target;
// no action for #B
if (el.id && /b/i.test(el.id)){ return true; }
alert(el.id || 'no id found');
}
// handler assignment (note: inline handler removed from html)
document.querySelector('#A').onclick = myHandler;
See it in action
Your z-index's won't work as you need to change the css position to relative, fixed, or absolute. reference.sitepoint.com/css/z-index.
<div id="A" style="width:100px; height: 100px; background: green; padding: 15px;
z-index: 50; opacity: .5; position:relative;" onclick="alert('A'); return false;">
<div id="B" style="width:100%; height:100%; background: red; z-index:100;position:relative;"
onclick="window.event.stopPropogation();alert('B'); return false;" >
</div>
</div>
http://jsfiddle.net/SmdK8/
I think using position: absolute in your styles and positioning one over the other would do this. Currently div A and div B sit side by side.
<div id="A" style="width:100px; height: 100px; background: #00FF00; padding: 15px;
z-index: 50; opacity: .5" onclick="javascript:alert('A')">
<div id="B" style="width:50px; height: 50px; background: #FF0000; z-index:10;"
onclick="javascript:event.preventDeafult();" >
</div>
</div>
Do a "preventDefault" based on when you don't want B to fire.
Here's one way to handle toggling B's onclick event
example: http://jsfiddle.net/pxfunc/cZtgV/
HTML:
<div id="A">A
<div id="B">B
</div>
</div>
<button id="toggle">Toggle B onclick</button>
JavaScript:
var a = document.getElementById('A'),
b = document.getElementById('B'),
toggleButton = document.getElementById('toggle'),
hasOnClick = true;
a.onclick = function() { alert('hi from A') };
b.onclick = function() { alert('hi from B') };
toggleButton.onclick = function() {
if (hasOnClick) {
b.onclick = "";
} else {
b.onclick = function() { alert('hi from B') };
}
hasOnClick = !hasOnClick;
};
for bonus points there's a jQuery solution in the example.

Categories