Why does mouseDown event not propagate in React? - javascript

I have a simple React component with two squares which are in the same absolute position. The blue square covers the red square.
function App() {
return (
<div className="App">
<div>
<div
style={{
position: "absolute",
backgroundColor: "red",
width: 100,
height: 100
}}
onMouseDown={() => console.log("Red!")}
/>
<div
style={{
position: "absolute",
backgroundColor: "blue",
width: 100,
height: 100
}}
onMouseDown={() => console.log("Blue!")}
/>
</div>
</div>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
When attaching event listeners to the react components, only the component on top catches the event and it does not reach the red square. Why is that the case? Is this default behavior? I thought event propagation is only stopped using event.stopPropagation()? When using vanilla javascript, both squares catch the event though.
function clickRed() {
console.log("Red!");
}
function clickBlue() {
console.log("Blue!");
}
<div id="root">
<div style="position: absolute; background-color: red; min-width: 100px; min-height: 100px;" onmousedown="clickRed();" />
<div style="position: absolute; background-color: blue; min-width: 100px; min-height: 100px;" onmousedown="clickBlue();"/>
</div>

This isn't a React thing, it's how events work in the DOM. The issue is that events propagate through the DOM tree, from innermost children to outermost ancestors of those children. But your elements are siblings, they don't have a parent/child relationship. So whichever one is above the other in the z-order will be the one that receives the event (which then propagates to that element's parent, not its siblings).
When using vanilla javascript, both squares catch the event though.
Not with the DOM structure you've defined in your question. Here it is with straight HTML and inline event handlers:
<div class="App">
<div>
<div
style="
position: absolute;
background-color: red;
width: 100px;
height: 100px;
"
onmousedown='console.log("Red!")'
></div>
<div
style="
position: absolute;
background-color: blue;
width: 100px;
height: 100px;
"
onmousedown='console.log("Blue!")'
></div>
</div>
</div>
Or with JavaScript using the DOM directly:
const app = document.createElement("div");
app.innerHTML = `
<div>
<div
style="
position: absolute;
background-color: red;
width: 100px;
height: 100px;
"
class="red"
></div>
<div
style="
position: absolute;
background-color: blue;
width: 100px;
height: 100px;
"
class="blue"
></div>
</div>
`;
app.querySelector(".red").addEventListener("mousedown", () => console.log("Red!"));
app.querySelector(".blue").addEventListener("mousedown", () => console.log("Blue!"));
document.getElementById("root").appendChild(app);
<div id="root"></div>
Re your edit adding this example:
function clickRed() {
console.log("Red!");
}
function clickBlue() {
console.log("Blue!");
}
<div id="root">
<div style="position: absolute; background-color: red; min-width: 100px; min-height: 100px;" onmousedown="clickRed();" />
<div style="position: absolute; background-color: blue; min-width: 100px; min-height: 100px;" onmousedown="clickBlue();"/>
</div>
The HTML there is incorrect. In HTML, <div/> is exactly the same thing as <div> — it's just a start tag. As a result, your div elements are nested (blue is inside red). The HTML equivalent to JSX <div/> is <div></div>:
function clickRed() {
console.log("Red!");
}
function clickBlue() {
console.log("Blue!");
}
<div id="root">
<!-- Scroll right in the stack snippet to see the difference at the end ⇒ ⇒ ⇩⇩⇩⇩⇩⇩⇩ -->
<div style="position: absolute; background-color: red; min-width: 100px; min-height: 100px;" onmousedown="clickRed();" ></div>
<div style="position: absolute; background-color: blue; min-width: 100px; min-height: 100px;" onmousedown="clickBlue();"></div>
</div>

Related

z-index issue for css and html [duplicate]

This question already has answers here:
Why can't an element with a z-index value cover its child?
(5 answers)
Closed 1 year ago.
**Here is a simple code **
div {
border: 1px solid blue;
}
.one {
background-color: red;
width: 300px;
height: 300px;
}
.two {
background-color: orange;
width: 300px;
height: 300px;
position: relative;
top: 100px;
left: 100px;
z-index: 10;
}
.twenty {
background-color: pink;
width: 300px;
height: 300px;
position: relative;
top: 100px;
left: 100px;
z-index: 500;
}
.three {
margin-top: -10px;
margin-left: 10px;
background-color: yellow;
width: 300px;
height: 300px;
}
.four {
background-color: green;
width: 300px;
height: 300px;
position: relative;
top: 80px;
left: 120px;
z-index: 11;
}
<div class="one">
<div class="two">
<div class="twenty">
Can pink be above green?
</div>
</div>
</div>
<div class="three">
<div class="four">
</div>
</div>
How to make the pink square to be above the green square? Is it possible with CSS changes only without HTML changes? Why z-index applied to the .twenty class doesn't work in this case?
Thank you.
This question is NOT a duplicate of Why can't an element with a z-index value cover its child? :
In this question we'd like the child to cover, not the parent!
All layers, except the twentieth, should not have Z order. We specify absolute an order and an order of a layer on Z.
.twenty{
position: absolute;
...
z-index: 1;
}
You should set .four with a lower z-index than .two which is the element that contains .twenty but this may not be the expected result (green also becomes under the orange).
It's not possible without changing the HTML structure, like putting .twenty inside .four, or redefining all the indexes. This is how the stacking context works.
Given this code:
<div class="A">
<div class="One"></div>
<div class="Two"></div>
</div>
<div class="B">
<div class="Three"></div>
</div>
From the top view
From the side view
Lear more about the stacking context:
CSS stacking contexts and z-index made easy
The stacking context
Yes, we can ;)... if orange gets a higher z-index than green...
.two {
...
z-index: 12;
}
It's because pink is a child of orange...

make the page scroll to put div on top

I'm building a page where I have some divs with different heights and a text inside them, I'd like to, when I click on div's text, to make the page move so that this div would get at the top of the screen.
I've searched around but what I find is often related to the fixed property, the thing is that I don't want to change the position property of the div, I'd like just the page to scroll automatically so that the div would be on top.
Do you have any advice on where I could start?
Thank you
.container {
width: 100vw;
height: auto;
}
.element {
padding: 50px;
}
#element-1 {
background-color: beige;
height: 500px;
}
#element-2 {
background-color: darkSeaGreen;
height: 200px;
}
#element-3 {
background-color: coral;
color: white;
height: 800px;
}
#element-4 {
background-color: MidnightBlue;
color: white;
height: 300px;
}
<div class="container">
<div class="element" id="element-1">
<h1>Some text</h1>
</div>
<div class="element" id="element-2">
<h1>Some text</h1>
</div>
<div class="element" id="element-3">
<h1>Some text</h1>
</div>
<div class="element" id="element-4">
<h1>Some text</h1>
</div>
</div>
If I correctly understand you, this will help you.
let divs = document.querySelectorAll(".element");
divs.forEach(div => {
div.addEventListener("click", event =>{
let divTop = div.offsetTop;
window.scrollTo(0, divTop);
console.log(divTop + " --- " + window.scrollY);
});
});
.container {
width: 100vw;
height: auto;
}
.element {
padding: 50px;
}
#element-1 {
background-color: beige;
height: 500px;
}
#element-2 {
background-color: darkSeaGreen;
height: 200px;
}
#element-3 {
background-color: coral;
color: white;
height: 800px;
}
#element-4 {
background-color: MidnightBlue;
color: white;
height: 300px;
}
<div class="container">
<div class="element" id="element-1">
<h1>Some text</h1>
</div>
<div class="element" id="element-2">
<h1>Some text</h1>
</div>
<div class="element" id="element-3">
<h1>Some text</h1>
</div>
<div class="element" id="element-4">
<h1>Some text</h1>
</div>
</div>
You would need to implement a function that does this
window.scrollTo(x, 0);
where x is the position of the element. You can get that by using
let x = element.getBoundingClientRect().top;

How to re-create Facebook image modal?

I want to create something almost exactly like the Facebook image modal wherein the image is fixed while a user scrolls through the comments. I am messing with different ways to apply overflow: hidden to one div and overflow: scroll to the other. I even looked into applying it to their parent. Here is the code I've tried:
<div class="row container border border-primary">
<div class="image col border">
Image
</div>
<div class="text-section col border">
Comments
</div>
</div>
div.image {
height: 300px;
overflow: hidden;
}
div.text-section {
height: 1000px;
overflow: scroll;
}
div.container {
height: 300px;
}
Plunkr
I supposed a code like this. The blue (image) remains fixed on the left, while you can scroll the green section (comments) on the right
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
#container { background: red; width: 400px; height: 150px; display: flex; }
#image { background: url("https://i1.adis.ws/i/canon/canon-pro-best-landscape-lenses-1-1140?w=200&aspect=4:3&qlt=70&sm=aspect&fmt=jpg&fmt.options=interlaced&fmt=jpg&fmt.options=interlaced&bg=rgb(255,255,255)"); width: 200px; height: 150px; }
#comments { background: #eee; width: 200px; overflow: scroll; padding: 0 10px 20px 10px; font-family: Verdana; color: black; }
</style>
</head>
<body>
<div id="container">
<div id="image"></div>
<div id="comments">
<h3 style="color: red;">Comments</h3>
<p>Nice!</p>
<p>Good!</p>
<p>Wonderful</p>
<p>Bah...</p>
<p>Strange</p>
<p>Nice again</p>
<p>Amazing</p>
<p>Beautiful</p>
<p>Great</p>
<p>I don’t like it</p>
<p>Yes, nice</p>
<p>Super</p>
<p>Normal</p>
<p>Ok...</p>
<p>Nice</p>
<p>Bah</p>
<p>Great</p>
<p>Nice</p>
<p>I like it</p>
<p>Normal</p>
</div>
</div>
</body>
</html>
I don't have facebook so cant look at the behaviour, but you could put position: sticky; on the image container, that will keep it in place. It also depends on your browser support, like ie11 does not support it, but there are more ways to do this. Let me know if you need a more cross browser solution.
.container {
max-height: 600px;
height: 100%;
overflow: auto;
position: relative;
}
div.image {
height: 300px;
background-color: deepskyblue;
position: sticky;
top: 0;
}
div.text-section {
height: 1000px;
background-color: aqua;
}
<div class="row container border border-primary">
<div class="image col border">
Image
</div>
<div class="text-section col border">
Comments
</div>
</div>

Multiple div's onhover same functionality

Here I have six different div on hover blue color div should appear and by default hidden. I have written code for this but it works only for the first div I merge all div's in a single variable. Can anyone suggest to me what I'm missing here
var tcpTooltip = $('.tp-cont-tech, tp-cont-b, tp-cont-m, tp-cont-t, tp-cont-i, tp-cont-e');
var tcpTooltipDiv = $('.tpc-tooltip-tech, tpc-tooltip-b, tpc-tooltip-m, tpc-tooltip-t, tpc-tooltip-i, tpc-tooltip-e');
tcpTooltipDiv.hide();
$(tcpTooltip).each(function() {
$(tcpTooltip).hover(function() {
$(tcpTooltipDiv).show();
}, function() {
$(tcpTooltipDiv).hide();
});
});
/* Tooltip */
.tp-cont-tech,
.tp-cont-e,
.tp-cont-t,
.tp-cont-m,
.tp-cont-i,
.tp-cont-b {
position: relative;
width: 200px;
height: 200px;
background-color: red;
margin-bottom: 20px;
}
.tpc-tooltip-tech,
.tpc-tooltip-e,
.tpc-tooltip-t,
.tpc-tooltip-m,
.tpc-tooltip-i,
.tpc-tooltip-b {
position: absolute;
top: 2%;
left: 5%;
z-index: 10;
width: 100px;
height: 100px;
background-color: blue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<div class="tpc-info">
<div class="tp-cont-tech">
<div class="tpc-tooltip-tech"></div>
</div>
<div class="tp-cont-b">
<div class="tpc-tooltip-b"></div>
</div>
<div class="tp-cont-m">
<div class="tpc-tooltip-m"></div>
</div>
<div class="tp-cont-t">
<div class="tpc-tooltip-t"></div>
</div>
<div class="tp-cont-e">
<div class="tpc-tooltip-e"></div>
</div>
</div>
As suggested already, I'd go by using pure CSS and the :hover pseudo.
If you really want jQuery for some reason this would be a remake of your code.
Basically (beside adding common classes to your elements [see code below]) you need the $(this) reference of the currently hovered element:
var $tpCont = $('.tp-cont');
var $tcpTooltip = $('.tcp-tooltip');
$tcpTooltip.hide();
$tpCont.hover(function() {
$(this).find($tcpTooltip).toggle();
});
.tp-cont {
position: relative;
width: 200px;
height: 200px;
background-color: red;
margin-bottom: 20px;
}
.tcp-tooltip {
position: absolute;
top: 2%;
left: 5%;
z-index: 10;
width: 100px;
height: 100px;
background-color: blue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<div class="tpc-info">
<div class="tp-cont tp-cont-tech">
<div class="tcp-tooltip tpc-tooltip-tech"></div>
</div>
<div class="tp-cont tp-cont-b">
<div class="tcp-tooltip tpc-tooltip-b"></div>
</div>
<div class="tp-cont tp-cont-m">
<div class="tcp-tooltip tpc-tooltip-m"></div>
</div>
<div class="tp-cont tp-cont-t">
<div class="tcp-tooltip tpc-tooltip-t"></div>
</div>
<div class="tp-cont tp-cont-e">
<div class="tcp-tooltip tpc-tooltip-e"></div>
</div>
</div>
You can achieve this far more effectively with CSS. If you add some common classes to the tp-cont-X and tpc-tooltip-X elements, then you can use the :hover pseudo-selector, like this:
.tp-cont {
position: relative;
width: 200px;
height: 200px;
background-color: red;
margin-bottom: 20px;
}
.tpc-tooltip {
position: absolute;
top: 2%;
left: 5%;
z-index: 10;
width: 100px;
height: 100px;
background-color: blue;
display: none;
}
.tp-cont:hover .tpc-tooltip {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<div class="tpc-info">
<div class="tp-cont tp-cont-tech">
<div class="tpc-tooltip tpc-tooltip-tech"></div>
</div>
<div class="tp-cont tp-cont-b">
<div class="tpc-tooltip tpc-tooltip-b"></div>
</div>
<div class="tp-cont tp-cont-m">
<div class="tpc-tooltip tpc-tooltip-m"></div>
</div>
<div class="tp-cont tp-cont-t">
<div class="tpc-tooltip tpc-tooltip-t"></div>
</div>
<div class="tp-cont tp-cont-e">
<div class="tpc-tooltip tpc-tooltip-e"></div>
</div>
</div>
Try using index inside hover.
var tcpTooltip = $('.tp-cont-tech, .tp-cont-b, .tp-cont-m, .tp-cont-t, .tp-cont-i, .tp-cont-e');
var tcpTooltipDiv = $('.tpc-tooltip-tech, .tpc-tooltip-b, .tpc-tooltip-m, .tpc-tooltip-t, .tpc-tooltip-i, .tpc-tooltip-e');
tcpTooltipDiv.hide();
$(tcpTooltip).each(function() {
$(tcpTooltip).hover(function(index, item) {
$(tcpTooltipDiv).eq($(this).index()).show();
}, function() {
$(tcpTooltipDiv).hide();
});
});
/* Tooltip */
.tp-cont-tech,
.tp-cont-e,
.tp-cont-t,
.tp-cont-m,
.tp-cont-i,
.tp-cont-b {
position: relative;
width: 200px;
height: 200px;
background-color: red;
margin-bottom: 20px;
}
.tpc-tooltip-tech,
.tpc-tooltip-e,
.tpc-tooltip-t,
.tpc-tooltip-m,
.tpc-tooltip-i,
.tpc-tooltip-b {
position: absolute;
top: 2%;
left: 5%;
z-index: 10;
width: 100px;
height: 100px;
background-color: blue;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<div class="tpc-info">
<div class="tp-cont-tech">
<div class="tpc-tooltip-tech"></div>
</div>
<div class="tp-cont-b">
<div class="tpc-tooltip-b"></div>
</div>
<div class="tp-cont-m">
<div class="tpc-tooltip-m"></div>
</div>
<div class="tp-cont-t">
<div class="tpc-tooltip-t"></div>
</div>
<div class="tp-cont-e">
<div class="tpc-tooltip-e"></div>
</div>
</div>

Dynamically added element function call not working

I have a problem with function call, when element is dynamically added.
Here is the link to code pen example. Function call - onclick - is from this button:
<button id="btnFocus" class="btnFocus" onclick="focusToDIV($(this))">Focus to DIV</button>
Function focusToDIV:
var focusToDIV = function(btnReference){
btnReference.parent().find("#div3").focus();
}
First element which contains this button is statically added. All the other elements can be added with button 'Add new'. With statically added element function focusToDIV gets called and div3 receives focus.
With dynamically added elements btnReference is not defined, that is why this error is thrown:
Uncaught TypeError: Cannot read property 'parent' of undefined
How to make this function work (put focus on div3 with click on btnFocus) with dynamically added elements? Why is btnReference not defined, if element is added dynamically to DOM?
var focusToDIV = function(btnReference){
btnReference.parent().find("#div3").focus();
}
var addNew = function(){
$("#divMain").append("<div class='divContainer' class='divContainer'> <div id='divInner' class='divInner'>" +
"<div id='div2' class='div2'> <div id='div3' class='div3' contentEditable='true'></div>" +
"</div></div> <button id='btnFocus' class='btnFocus' onclick='focusToDIV($(this))'>Focus to DIV</button> </div>");
}
.divMain{
height: 100vh;
width: 100vw;
}
.divContainer{
position: relative;
display: inline-block;
left: 20%;
top: 10%;
}
.divInner{
width: 300px;
height: 300px;
border: 1px solid black;
}
.div2{
position: relative;
left: 20px;
top: 20px;
width: 200px;
height: 100px;
border: 1px solid blue;
}
.btnFocus{
position: absolute;
top: 305px;
}
.div3{
position: relative;
left: 10%;
top: 10%;
width: 150px;
height: 80px;
border: 1px solid red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="divMain" class="divMain">
<button id="btnAdd" onclick="addNew()">Add new</button>
<div class="divContainer" class="divContainer">
<div id="divInner" class="divInner" >
<div id="div2" class="div2">
<div id="div3" class="div3" contentEditable="true"></div>
</div>
</div>
<button id="btnFocus" class="btnFocus" onclick="focusToDIV($(this))">Focus to DIV</button>
</div><!-- divContainer -->
</div><!-- divMain -->
You need to change the onclick method from focusToDIV() to focusToDIV($(this)).
As the element wasn't passed btnReference is undefined and you were calling a method on that undefined variable.
var addNew = function(){
$("#divMain").append("<div class='divContainer' class='divContainer'> <div id='divInner' class='divInner'>" +
"<div id='div2' class='div2'> <div id='div3' class='div3' contentEditable='true'></div>" +
"</div></div> <button id='btnFocus' class='btnFocus' onclick='focusToDIV($(this))'>Focus to DIV</button> </div>");
}

Categories