I have 2 sibling-nodes with 'position absolute' which both handle mousedown event. How can I trigger the handler of 'div 1' when I am clicking on the transparent area of the 'div 2' (on the pic.)
If the overlapping elements are dynamic, I don't believe it is possible to accomplish this using regular event bubbling since the two overlapping elements in question are "siblings".
I had the same problem and I was able to solve it with more of a hitTest scenerio where I test if the user's mouse position is within the same area.
function _isMouseOverMe(obj){
var t = obj.offset().top;
var o = obj.offset().left;
var w = obj.width();
var h = obj.height();
if (e.pageX >= o+1 && e.pageX <= o+w){
if (e.pageY >= t+1 && e.pageY <= t+h){
return true;
}
}
return false
}
You'll want to use 3 event handlers, one for div1, one for div2, and one for contentArea. The contentArea handler should stop propagation so that the div2 handler is not called. The div2 handler should call the div1 handler:
function div1Click (e)
{
// do something
}
function div2Click (e)
{
div1Click.call(div1, e);
}
function contentAreaClick (e)
{
e = e || window.event;
if (e.stopPropagation) e.stopPropagation();
e.cancelBubble = true;
// do something
}
div1.onclick = div1Click;
div2.onclick = div2Click;
contentArea.onclick = contentAreaClick;
What you were looking was CSS's pointer-events property. I didn't make a research to learn whether it was available at the times the question been asked, but that I faced the same thing I'm taking a liberty of covering it.
Here're your two DIVs:
<body>
<div class="inner div-1"></div>
<div class="inner div-2">
<div class="div-2__content-area"></div>
</div>
</body>
Styling:
.inner {
height: 10em;
position: absolute;
width: 15em;
}
.div-1 {
/* Intrinsic styling & initial positioning, can be arbitrary */
background: #ff0;
left: 50%;
top: 50%;
transform: translate(-75%, -75%);
}
.div-2 {
/* Intrinsic styling & initial positioning, can be arbitrary */
background: rgba(0, 255, 0, 0.25);
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
/* Centering content area */
align-items: center;
display: flex;
justify-content: center;
/* Key feature -- making visible part of transparent DIV insensitive to mouse (pointer) events), letting them fire on the underlying element */
pointer-events: none;
}
.div-2__content-area {
/* Styling content area */
background: #f00;
height: 75%;
width: 75%;
/* Reverting 'pointer-events' perception to regular */
pointer-events: auto;
}
Event listeners and displaying:
document.querySelector(".div-1").addEventListener("mousedown", (_) => {
alert("div 1");
});
document.querySelector(".div-2__content-area").addEventListener("mousedown", (_) => {
alert("Content area");
});
This all on codepen:
https://codepen.io/Caaat1/pen/RwZEJVP
Related
I have a div with a scroll event on it. The scroll event is a sync function that takes some time to be computed. I see that the navigators might react to this situation with 2 different behaviours.
1- The scroll is freezed until the scroll event ends and then it will trigger the next scroll event.
2- I can scroll fluidly, but the scroll event is triggered once the previous event is done.
I would like to know how can I control and decide which one of the two scenarios the user will face.
codepen: https://codepen.io/xmorelll/pen/wvdmbYq
const container = document.getElementById("container");
const text = document.getElementById("text");
container.addEventListener('scroll', (event) => {
//sleep 1Second to simulate that the program is busy.
let a = new Date().getTime();
while(new Date().getTime() < a + 1000){};
text.style.top = container.scrollTop + "px";
});
#container {
height: 300px;
width: 300px;
border: solid 1px black;
overflow: auto;
}
#content {
height: 1000px;
width: 100%;
background-image: linear-gradient(red, yellow);
position: relative;
}
#text {
position: absolute;
left: 0;
}
<div id="container">
<div id="content">
<span id="text">Hola</span>
</div>
</div>
I'm trying to make a fake/duplicated scroll element that is synced with the actual x-scroll of a div in native javaScript. The use case for me is on a long table to have the x-scroll be present on screen when you're not at the bottom of the table.
This solves the situation of having a really wide table with a min-width that exceeds the current page/view-port width, causing the table to side/x-scroll. However, if the table is very long, the scroll can only be set on top or bottom of the table. That means if people are mid-way down the table and want to scroll across to see all of the columns, they would have difficulty in doing it there.
See image:
Yep, it's been done to death IN JQUERY. According to my research, no one on SO knows or has been interested in doing this in native javaScript (esp 2020). My version for reference is also in jQuery, it needs to be converted.
$dupScroll.scroll(function () {
if (scrolling) {
scrolling = false;
return true;
}
scrolling = true;
$tableParent.scrollLeft($dupScroll.scrollLeft());
});
$tableParent.scroll(function () {
if (scrolling) {
scrolling = false;
return true;
}
scrolling = true;
$dupScroll.scrollLeft($tableParent.scrollLeft());
});
All the jQuery solutions:
How to Scroll two div's at the same time?
synchronizing scrolling between 2 divs
synchronizing scrolling between 2 divs with different text size
How to sync the scroll of two divs by ID
synchronise scrolling of two divs
Help is appreciated and will be useful for all the people needing to do the same post-jQuery. I'm currently working on this myself but running into snags here and there, the 1st being attaching a scroll event onto an element. If I make something that works, I'll post it here. Thanks.
Here's the simple way to keep two divs aligned. Javascript doesn't dispatch event on actions from scripts by default, so there's no need to keep track of which div is being scrolled.
const divs = document.querySelectorAll( 'div' );
divs.forEach(div => div.addEventListener( 'scroll', e => {
divs.forEach(d => {
d.scrollTop = div.scrollTop;
d.scrollLeft = div.scrollLeft;
});
}) );
html, body {
height: 100%;
}
body {
display: flex;
padding: 0;
margin: 0;
}
div {
width: 50%;
height: 100%;
overflow: scroll;
}
span {
width: 200vw;
height: 300vh;
display: block;
background: linear-gradient(90deg, transparent, yellow), linear-gradient( 0deg, red, blue, green );
}
#div2 {
margin-top: 30px;
}
<div id="div1"><span></span></div>
<div id="div2"><span></span></div>
With Relative Scroll in Different Sized Containers
If you want to accomplish this with differently sized containers and relative scroll, just normalize the scroll value and multiply it again:
const divs = document.querySelectorAll( 'div' );
divs.forEach(div => div.addEventListener( 'scroll', e => {
const offsetTop = div.scrollTop / (div.scrollHeight - div.clientHeight);
const offsetLeft = div.scrollLeft / (div.scrollWidth - div.clientWidth);
divs.forEach(d => {
d.scrollTop = offsetTop * (d.scrollHeight - d.clientHeight);
d.scrollLeft = offsetLeft * (d.scrollWidth - d.clientWidth);
});
}) );
html, body {
height: 100%;
}
body {
display: flex;
padding: 0;
margin: 0;
}
div {
width: 50%;
height: 100%;
overflow: scroll;
}
span {
width: 200vw;
height: 300vh;
display: block;
background: linear-gradient(90deg, transparent, yellow), linear-gradient( 0deg, red, blue, green );
}
#div2 span {
height: 500vh;
width: 500vw;
}
<div id="div1"><span></span></div>
<div id="div2"><span></span></div>
I just found this and can't explain myself why it behaves like this. But this is exactly the behavior I want to implement. Try this example:
const redBar = document.querySelector("#red");
const greenBar = document.querySelector("#green");
redBar.onmousemove = moveHandle;
redBar.onclick = moveHandle;
greenBar.onmousemove = moveHandle;
greenBar.onclick = moveHandle;
function moveHandle(event) {
if (event.buttons === 1 || event.type === "click") {
let y = (event.y - this.offsetTop) - 7.5;
if (y < -7.5) {
y = -7.5;
} else if (y > this.offsetHeight - 7.5) {
y = this.offsetHeight - 7.5;
}
this.querySelector(".handle").style.top = y + "px";
}
}
body {
display: flex;
width: 100vw;
height: 100vh;
justify-content: center;
align-items: center;
margin: 0;
}
.container {
display: flex;
flex-direction: row;
}
.bar {
position: relative;
background: red;
overflow: visible;
margin: 30px;
width: 30px;
height: 200px;
}
.no-overflow {
background: green;
overflow: hidden;
}
.handle {
position: relative;
width: 60px;
height: 15px;
top: -7px;
left: -15px;
background: #222d;
}
<div class="container">
<div id="red" class="bar">
<div class="handle"></div>
</div>
<div id="green" class="no-overflow bar">
<div class="handle"></div>
</div>
</div>
Try moving the handle of the red bar and of the green bar. On the green bar you can move the handle once you had the mouse down on the bar and keep holding down the button, even if you are not above the bar.
The only difference is that on the green one (except the color) the overflow
is hidden.
Is this a bug or did I forget something? (I'm on Firefox btw)
And whats the best way to implement a behavior like this without the overflow being hidden because when i try to do this, it always get pretty messy. (I know there are sliders but maybe in case I want something like this with 2 dimensions)
Thanks!
UPDATE
By further testing, I noticed that this stops working when user-select is none. Then it behaves like the element without the hidden overflow.
The mousemove misfiring in Firefox due to overflow:hidden looks like bug 620513 / bug 1352061.
As for achieving this effect without relying on the bug, Teemu correctly notes that:
Usually this is done by listening mousedown on the element, and when that fires, stop listening mousedown and start to listen mouseup and mousemove on document. When mouseup fires, stop listening mouseup and mousemove, and start listening mousedown on the element again.
Pointer Events API (.setPointerCapture()) is the modern way to achieve this behavior (needs a polyfill for Safari and older browsers).
I have container and I want to resize it with mouse using JavaScript.
the implementation uses mousedown ,mouseup, and mousemove events but I have slight problem when page is already scrolled before the mousedown is fired.
the whole page seems to scroll to position zero. How can i fix it so whether page is already scrolled or not the resize will work fine.
JS:
//resize div vertically or horizontal or both
$.fn.resizeMe = function(options){
var grippie = $(this),
options = $.extend({resizeMe:"",resize:"vertical"},options),
resizeMe = $(options.resizeMe);
grippie.on('mousedown',function(e){initialiseGrippieResize(e)});
function initialiseGrippieResize(e) {
$(window).on('mousemove',function(e){
startResizing(e);
resizeMe.css({opacity:.25});
}).on('mouseup',function(e){
stopResizing(e);
resizeMe.css({opacity:1});
});
}
//css objects
function cssOBJ(e,key){
var css = {
vertical:{height:(e.clientY - resizeMe.offset().top)},
horizontal : {width:(e.clientX - resizeMe.offset().left)},
both: {
height:(e.clientY - resizeMe.offset().top),
width: (e.clientX - resizeMe.offset().left)
}
};
//return objects
return css[key];
}
//Start Resizing
function startResizing(e) {
resizeMe.css(cssOBJ(e,options.resize));
}
function stopResizing(e) {
$(window).off('mousemove mouseup');
}
}
$('.grippie').resizeMe({resizeMe:"#pane",resize:'vertical'});
HTML:
<div id='main-container'>
<div id='pane' arial-lable='content-wrapper'>contents will go here</div>
<div class='grippie'></div>
</div>
CSS:
#pane{
resize: n-resize;
overflow: hidden;
border: 1px solid #d6d9dc;
padding:15px 20px;
min-height:50px;
}
#pane *{border:0px !important; background: #fff !important;}
.grippie {
background-position: center;
border: 1px solid #d6d9dc;
border-width: 0 1px 1px;
cursor: s-resize;
height: 9px;
overflow: hidden;
background-color: #eff0f1;
/*background-image: url('images/icons.png');*/
background-repeat: no-repeat;
In JavaScript
pageX, pageY, screenX, screenY, clientX, and clientY returns a number which indicates the number of physical “CSS pixels” a point is from the reference point. The event point is where the user moved the mouse, the reference point is a point in the upper left. These properties return the horizontal and vertical distance from that reference point.
but pageX or pageY returns a number which indicates the amount of page scrolled top or left.
so changing the clientX to pageX and clientY to pageY
solved the problem and now I can resize any element. :) :)
//css objects
function cssOBJ(e,key){
var css = {
vertical:{height:(e.pageY - resizeMe.offset().top)},
horizontal : {width:(e.pageX - resizeMe.offset().left)},
both: {
height:(e.pageY - resizeMe.offset().top),
width: (e.pageX - resizeMe.offset().left)
}
};
//return objects
return css[key];
}
I have a set of buttons shown on the screen. I am trying to implement a function which executes upon hovering on a button (having a class 'button').
This function needs to display the mouse pointer at the center of the button, meaning that whilst the actual mouse pointer is on the button, the mouse pointer is displayed in the center of the button. I tried using the RequestPointerLock API but this hides the mouse pointer whereas I want it to be displayed and I believe it only works with the event handler onclick.
This is what I've done so far:
var buttons = document.getElementsByClassName('button');
buttons.requestPointerLock = buttons.requestPointerLock || buttons.mozRequestPointerLock;
document.exitPointerLock = document.exitPointerLock || document.mozExitPointerLock;
document.addEventListener('pointerlockchange', lockChangeAlert, false);
document.addEventListener('mozpointerlockchange', lockChangeAlert, false);
function lockChangeAlert()
{
if (document.pointerLockElement === buttons || document.mozPointerLockElement === buttons)
{
console.log('The pointer lock status is now locked');
document.addEventListener("mousemove", updatePosition, false);
} else
{
console.log('The pointer lock status is now unlocked');
document.removeEventListener("mousemove", updatePosition, false);
}
}
function updatePosition(e)
{
}
buttons.onclick = function()
{
buttons.requestPointerLock();
}
Any ideas on how I can implement this please?
There is no way for you to set the position of the cursor using JavaScript. This goes against a number of security considerations. The best you can do is hide the cursor (with cursor: none on :hover, and draw a static image of one on the center of the button.
Please note that the inconsistency of the cursor icons on different systems will prove to be a problem if you choose to go down this path.
As it is with a few other JavaScript APIs, requestPointerLock doesn't work with events such as mouseover (not unlike requestFullscreen). They need direct user interaction to work. An event such as mouseover would be exploited too easily.
The Pointer Lock API example uses a canvas to draw a circle to a canvas.
This might be helpful.
button {
background: #333;
position: relative;
color: #fff;
padding: 40px 80px;
border: none;
font-size: 32px;
}
button:hover {
cursor: none;
}
button:hover:after {
content: '';
background-image: url("http://www.freeiconspng.com/uploads/original-file---svg-file-nominally-550--400-pixels-file-size-3--23.png");
width: 48px;
height: 48px;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background-size: contain;
background-repeat: no-repeat;
}
<button>Here</button>
Here is also the codepen: http://codepen.io/hunzaboy/pen/pNwOqZ