Event-bubbling with z-indices - javascript

JSFiddle: https://jsfiddle.net/uLap7yeq/19/
Problem
Consider two elements, canvas and div, which are on the same tree depth and have the same parent. They are both placed on the same position using CSS however div has the higher z-index. How can you capture events from the div and pass them along to the lower z-index? Do I have to do .dispatchEvent() on the canvas?
EDIT: To clarify, I'd like the div to receive the event, do whatever it wants and then pass it along to the next z-indexed element.
The JSFiddle pasted inline:
/*
How can I pass the event along to #canvas?
*/
$('#container').on('click', function(e) {
console.log('#container click');
});
$('#canvas').on('click', function(e) {
console.log('#canvas click');
});
$('#other-div').on('click', function(e) {
console.log('#other-div click');
});
#other-div {
z-index: 1;
position: absolute;
top: 0;
left: 0;
}
#canvas {
z-index: 0;
position: absolute;
top: 0;
left: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="container">
<div id="other-div">
<p>
Something
</p>
</div>
<canvas id="canvas" width="200" height="200"></canvas>
</div>

Adding css property pointer-events: none; to the #other-div will let clicks or other pointer-related events to pass through the div and reach both the canvas and the container
#other-div {
z-index: 1;
position: absolute;
top: 0;
left: 0;
pointer-events: none;
}
See Fiddle 1
If this is not suitable because you need that the other-div too captures the event (as per your comment) then you may trigger programmatically an event on the canvas when the container is clicked
$('#container').on('click', function(e) {
console.log('#container click');
$('#canvas').click(); // <------
});
$('#canvas').on('click', function(e) {
e.stopImmediatePropagation(); // <------
console.log('#canvas click');
});
$('#other-div').on('click', function(e) {
console.log('#other-div click');
});
When the cointainer receives a click then it triggers a click on the undelying canvas too: $('#canvas').click();
Note that when the click finally hits the canvas the event must stop propagate, otherwise it will bubble and hit both the #other-div and the #container leading to an infinite loop. This is why you have e.stopImmediatePropagation();
See Fiddle 2

You can trigger a custom event when you click on the outer-div and make the canvas listening to this event:
$('#container').on('click', function(e) {
console.log('#container click');
});
$('#canvas').on('click custom', function(e) {
console.log('#canvas click');
});
$('#other-div').on('click', function(e) {
console.log('#other-div click');
$('#canvas').trigger( "custom");
});
#other-div {
z-index: 1;
position: absolute;
top: 0;
left: 0;
background:rgba(255,0,0,0.2);
}
#canvas {
z-index: 0;
position: absolute;
top: 0;
left: 0;
background:rgba(255,255,0,0.2);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="container">
<div id="other-div">
<p>
Something
</p>
</div>
<canvas id="canvas" width="200" height="200"></canvas>
</div>

You can use a dynamic approach by patching the Element prototype:
if (!document.elementsFromPoint) {
document.elementsFromPoint = elementsFromPoint;
}
function elementsFromPoint(x, y) {
var parents = [];
var parent = void 0;
do {
if (parent !== document.elementFromPoint(x, y)) {
parent = document.elementFromPoint(x, y);
parents.push(parent);
parent.style.pointerEvents = 'none';
} else {
parent = false;
}
} while (parent);
parents.forEach(function (parent) {
parent.style.pointerEvents = 'initial';
});
return parents;
}
Element.prototype.makeEventGoThrough = function(eventName) {
$(this).on(eventName, (e) => {
var elements = document.elementsFromPoint(e.clientX, e.clientY);
var children = [].slice.call(this.children);
elements = elements.filter(element => element !== this && !children.find(el => el === element));
elements.forEach(element => $(element).trigger(eventName));
});
}
/*
How can I pass the event along to #canvas?
*/
document.getElementById('other-div').makeEventGoThrough('click');
$('#other-div').on('click', () => console.log('other-div clicked'));
$('#canvas').on('click', () => console.log('canvas clicked'));
#other-div {
z-index: 1;
position: absolute;
top: 0;
left: 0;
}
#canvas {
z-index: 0;
position: absolute;
top: 0;
left: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="container">
<div id="other-div">
<p>
Something
</p>
</div>
<canvas id="canvas" width="200" height="200"></canvas>
</div>

Related

Is it possible to fire mouse events without mouse moves in safari?

I'm trying to put mouse enter/mouse-leave events on moving elements. There is a problem in handling the hover event with CSS, so I'm trying to process it with JS. This works fine in chrome but not working in safari. How to make mouseenter/mouseleave work properly when the mouse is stopped?
CSS
.big-box {
position: relative;
width: 500px;
height: 100px;
background: #ee9;
}
.hit-box {
position: absolute;
top: 0;
left: 0;
width: 100px;
height: 100px;
background: #e99;
}
HTML
<div class="big-box">
<div class="hit-box">hit-box</div>
</div>
JavaScript
const hitbox = document.querySelector('.hit-box');
var position = 5;
hitbox.style.left = 0;
hitbox.addEventListener('mouseenter', function() {
console.log('enter');
});
hitbox.addEventListener('mouseleave', function() {
console.log('leave');
});
setInterval(function() {
if(parseInt(hitbox.style.left) + position > 400) {
position = -5;
} else if(parseInt(hitbox.style.left) + position < 0) {
position= 5;
}
hitbox.style.left = parseInt(hitbox.style.left) + position + 'px';
},20)

Smoothly scroll element from current position

I have written the following code to scroll an element 20 more pixels to the right.
const button = document.getElementById('slide');
button.onclick = function () {
document.getElementById('container').scrollLeft += 20;
};
How can I make the scrolling smooth? I have tried using Element#scroll like so:
const button = document.getElementById('slide');
button.onclick = function () {
document.getElementById('container').scroll({
left: += 20,
behavior: smooth
});
};
Am I able to do this?
You can use Element#scrollBy to scroll a certain amount from the current position.
button.onclick = function () {
document.getElementById('container').scrollBy({
left: 20,
behavior: 'smooth'
});
};
Is this what you are looking for?
From MDN:
scrollBy() scrolls by a particular amount, whereas scroll() scrolls to an absolute position in the document.
https://developer.mozilla.org/en-US/docs/Web/API/Window/scrollBy
const container = document.querySelector("#container");
const button = document.querySelector("#btnScroll");
button.addEventListener('click', e => {
container.scrollBy({
left: 200,
behavior: 'smooth'
});
});
#container {
position: relative;
max-width: 200px;
overflow-x: scroll;
display: flex;
margin: 20px;
}
img {
display: inline-block
}
<div id="container">
<img src="https://dummyimage.com/200x100/000/fff">
<img src="https://dummyimage.com/200x100/0f0/000">
<img src="https://dummyimage.com/200x100/00f/fff">
<img src="https://dummyimage.com/200x100/f00/fff">
</div>
<button id="btnScroll">Scroll 200px</button>

How to get the start value and end value of a div after dragging

Hi i have a div which is draggable .My requirement is
1. I want the start value and end value of the div after dragging in a text box
2.Now its is only drag in right side only i want it drag also from left side of the div
i tried some steps but it is not perfect because the value is not displaying and the dragging from left is not working middle table is my parent div
$(function () {
var container = $('.middletablediv'),
base = null,
handle = $('.handle'),
isResizing = false,
screenarea = screen.width;
handle.on('mousedown', function (e) {
base = $(this).closest(".scalebar");
isResizing = true;
lastDownX = e.clientX;
offset = $(this).offset();
xPos = offset.left;
});
$(document).on('mousemove', function (e) {
// we don't want to do anything if we aren't resizing.
if (!isResizing)
return;
p = parseInt(e.clientX - base.offset().left),
// l = parseInt(p * (3 / 11));
base.css('width', p);
k = parseInt(xPos - base.offset().left);
$("#startvalue").value(k)
$("#stopvalue").value(p)
}).on('mouseup', function (e) {
// stop resizing
isResizing = false;
});
});
.handle{
position: absolute;
top:1px;
right: 0;
width: 10px;
height: 5px;
cursor: w-resize;
}
.middletablediv{
float:left;
width:35%;
}
.scalebar{
margin-top: 13px;
height: 7px;
position: relative;
width:20px;
background-color: green; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="middletablediv">
<div id="newvalue1" class="scalebar">
<div class="handle" style="left:0"></div> <div class="handle"></div>
</div>
</div>
<input id="startvalue" type="text">startvalue</input>
<input id="stopvalue" type="text" />stopvalue</input>
how i solve this issue
You should use val() instead of value(). Also, the way dragging works the end value can be smaller than the start value, so I added a couple of things to handle that problem in a simple way (just switch values). Finally, to drag from the left, you should handle the left dragging differently, so I gave the left handle a unique id and also padded the whole parent div a bit to make it more apparent.
$(function () {
var container = $('.middletablediv'),
base = null,
handle = $('.handle'),
isResizing = false,
isLeftDrag = false;
screenarea = screen.width;
handle.on('mousedown', function (e) {
base = $(this).closest(".scalebar");
isResizing = true;
if($(this).attr('id')=='lefthandle')isLeftDrag=true;
else isLeftDrag=false;
lastDownX = e.clientX;
offset = $(this).offset();
xPos = offset.left;
});
$(document).on('mousemove', function (e) {
// we don't want to do anything if we aren't resizing.
if (!isResizing)
return;
if(isLeftDrag){
p = parseInt(base.offset().left - e.clientX);
k = parseInt(base.offset().left - xPos);
base.css('margin-left',-p);
base.css('width',p);
}
else{
p = parseInt(e.clientX - base.offset().left),
// l = parseInt(p * (3 / 11));
base.css('width', p);
k = parseInt(xPos - base.offset().left);
}
//if(k>p){var temp = k; k = p; p = temp;}
$("#startvalue").val(k)
$("#stopvalue").val(p)
}).on('mouseup', function (e) {
// stop resizing
isResizing = false;
});
});
.handle{
position: absolute;
top:1px;
right: 0;
width: 10px;
height: 5px;
cursor: w-resize;
}
.middletablediv{
float:left;
width:35%;
}
.scalebar{
margin-top: 13px;
height: 7px;
position: relative;
width:20px;
background-color: green; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="middletablediv" style="padding-left:100px; overflow:visible;">
<div id="newvalue1" class="scalebar">
<div class="handle"id="lefthandle" style="left:0"></div> <div class="handle"></div>
</div>
</div><br><br>
<input id="startvalue" type="text">startvalue</input>
<input id="stopvalue" type="text" />stopvalue</input>

Make div expandable when I drop an image on it

Check out this fiddle, here I can drag and drop images on .drop-zone but I want to improve it such that if I drag image on it, the .drop-zone div expands to the position where I am putting the image. Scrollable would be fine.
<div class="drag"><img src="http://www.causingeffect.com/images/made/images/example/cow_50_50_c1.jpg" /></div>
<div class="drag"><img src="http://www.coffeecup.com/images/software/feature-icons/image_editing.png" /></div>
<div class="drag"><img src="http://www.neatimage.com/im/Neat-Image-Logo-64.png" /></div>
<div class="clear"></div>
<div class="drop-zone"></div>
JS
jQuery(function($) {
$('.drop-zone').droppable({
accept: '.drag',
drop: function(event, ui) {
var $clone = ui.helper.clone();
if (!$clone.is('.inside-drop-zone')) {
$(this).append($clone.addClass('inside-drop-zone').draggable({
containment: '.drop-zone'
}));
}
}
});
$('.drag').draggable({
helper: 'clone'
});
});
CSS
.drop-zone{border: 1px solid #9C9898;height:350px;width:400px;margin:0 auto;margin-top: 10px; overflow:auto;}
.clear{clear:both;}
You need some way to detect those edges. I'm using some child divs inside .drop-zone. Next problem is that you need .drop-zone to have position: relative and that messes up the drag/drop library. I applied the fix described here.
Next you find out that mouseover no longer propagates through the .drag element while dragging it, so you need to make sure .drop-zone-edge divs are above it, thus the custom z-index values. Try it out here.
Apply the same technique for top, left and right and make .drop-zone-edges transparent.
HTML:
<div class="drop-zone">
<div class="drop-zone-edge drop-zone-top"></div>
<div class="drop-zone-edge drop-zone-bottom"></div>
</div>
CSS:
.drag {
z-index: 100;
}
.drop-zone-edge {
width: 100%;
height: 10px;
background-color: red;
position: absolute;
z-index: 200;
}
.drop-zone-top {
top: 0;
}
.drop-zone-bottom {
bottom: 0;
}
JS:
var isDown = false;
$(document).mousedown(function() {
isDown = true;
})
.mouseup(function() {
isDown = false;
});
$('.drop-zone').droppable({
accept: '.drag',
drop: function(event, ui) {
var $clone = ui.helper.clone();
if (!$clone.is('.inside-drop-zone')) {
$(this).append($clone.addClass('inside-drop-zone').draggable({
containment: '.drop-zone'
}));
}
var parent = $('.drop-zone');
var leftAdjust = $clone.position().left - parent.offset().left;
var topAdjust = $clone.position().top - parent.offset().top;
$clone.css({left: leftAdjust, top: topAdjust});
}
});
$('.drag').draggable({
helper: 'clone'
});
var shouldEnlargeBottom = false;
$('.drop-zone-bottom').on('mouseover', function(e) {
if(isDown) {
shouldEnlargeBottom = true;
}
});
$('.drop-zone-bottom').on('mouseout', function(e) {
shouldEnlargeBottom = false;
});
function enlarge() {
if(shouldEnlargeBottom) {
var newHeight = $('.drop-zone').height() + 3;
$('.drop-zone').height(newHeight);
}
}
setInterval(enlarge, 100);

How to toggle (hide / show) sidebar div using jQuery

I have 2 <div>s with ids A and B. div A has a fixed width, which is taken as a sidebar.
The layout looks like diagram below:
The styling is like below:
html, body {
margin: 0;
padding: 0;
border: 0;
}
#A, #B {
position: absolute;
}
#A {
top: 0px;
width: 200px;
bottom: 0px;
}
#B {
top: 0px;
left: 200px;
right: 0;
bottom: 0px;
}
I have <a id="toggle">toggle</a> which acts as a toggle button. On the toggle button click, the sidebar may hide to the left and div B should stretch to fill the empty space. On second click, the sidebar may reappear to the previous position and div B should shrink back to the previous width.
How can I get this done using jQuery?
$('button').toggle(
function() {
$('#B').css('left', '0')
}, function() {
$('#B').css('left', '200px')
})
Check working example at http://jsfiddle.net/hThGb/1/
You can also see any animated version at http://jsfiddle.net/hThGb/2/
See this fiddle for a preview and check the documentation for jquerys toggle and animate methods.
$('#toggle').toggle(function(){
$('#A').animate({width:0});
$('#B').animate({left:0});
},function(){
$('#A').animate({width:200});
$('#B').animate({left:200});
});
Basically you animate on the properties that sets the layout.
A more advanced version:
$('#toggle').toggle(function(){
$('#A').stop(true).animate({width:0});
$('#B').stop(true).animate({left:0});
},function(){
$('#A').stop(true).animate({width:200});
$('#B').stop(true).animate({left:200});
})
This stops the previous animation, clears animation queue and begins the new animation.
You can visit w3school for the solution on this the link is here and there is another example also available that might surely help,
Take a look
The following will work with new versions of jQuery.
$(window).on('load', function(){
var toggle = false;
$('button').click(function() {
toggle = !toggle;
if(toggle){
$('#B').animate({left: 0});
}
else{
$('#B').animate({left: 200});
}
});
});
Using Javascript
var side = document.querySelector("#side");
var main = document.querySelector("#main");
var togg = document.querySelector("#toogle");
var width = window.innerWidth;
window.document.addEventListener("click", function() {
if (side.clientWidth == 0) {
// alert(side.clientWidth);
side.style.width = "200px";
main.style.marginLeft = "200px";
main.style.width = (width - 200) + "px";
togg.innerHTML = "Min";
} else {
// alert(side.clientWidth);
side.style.width = "0";
main.style.marginLeft = "0";
main.style.width = width + "px";
togg.innerHTML = "Max";
}
}, false);
button {
width: 100px;
position: relative;
display: block;
}
div {
position: absolute;
left: 0;
border: 3px solid #73AD21;
display: inline-block;
transition: 0.5s;
}
#side {
left: 0;
width: 0px;
background-color: red;
}
#main {
width: 100%;
background-color: white;
}
<button id="toogle">Max</button>
<div id="side">Sidebar</div>
<div id="main">Main</div>
$('#toggle').click(function() {
$('#B').toggleClass('extended-panel');
$('#A').toggle(/** specify a time here for an animation */);
});
and in the CSS:
.extended-panel {
left: 0px !important;
}
$(document).ready(function () {
$(".trigger").click(function () {
$("#sidebar").toggle("fast");
$("#sidebar").toggleClass("active");
return false;
});
});
<div>
<a class="trigger" href="#">
<img id="icon-menu" alt='menu' height='50' src="Images/Push Pin.png" width='50' />
</a>
</div>
<div id="sidebar">
</div>
Instead #sidebar give the id of ur div.
This help to hide and show the sidebar, and the content take place of the empty space left by the sidebar.
<div id="A">Sidebar</div>
<div id="B"><button>toggle</button>
Content here: Bla, bla, bla
</div>
//Toggle Hide/Show sidebar slowy
$(document).ready(function(){
$('#B').click(function(e) {
e.preventDefault();
$('#A').toggle('slow');
$('#B').toggleClass('extended-panel');
});
});
html, body {
margin: 0;
padding: 0;
border: 0;
}
#A, #B {
position: absolute;
}
#A {
top: 0px;
width: 200px;
bottom: 0px;
background:orange;
}
#B {
top: 0px;
left: 200px;
right: 0;
bottom: 0px;
background:green;
}
/* makes the content take place of the SIDEBAR
which is empty when is hided */
.extended-panel {
left: 0px !important;
}

Categories

Resources