I'm trying to toggle a second element by clicking on the first, and not having the second as interactive but it's not working. What am I doing wrong? The element.timage should change itself and the element . rimage when selected, but only the element.timage should be clickable.
function myFunction(x) {
if (x.target.matches('.timage'))
this.classList.toggle('change');
}
document.querySelector('.container4').addEventListener('click', myFunction);
.container4 {
cursor: pointer;
display: inline- block;
}
.timage {
position: relative;
left: 50px;
}
.change .timage {
left: 200px;
}
.rimage {
position: relative;
left: 200px;
}
.change .rimage {
position: relative;
left 500px;
}
<a id="mobile-nav" href="#">
<div class="container4" onclick="myFunction
(this,event)">
<div class="container4">
<div class="timage"><img class="size-medium wp-
image-13846" src="http://4309.co.uk/wp-
content/uploads/2020/05/
IMG_20200509_104613-
288x300.jpg" alt="" width="70" height="300" />.
</div>
<div class="rimage">
<img class="size-medium wp-
image-13669" src="http://4309.co.uk/
wp-content/uploads
/2020/05/IMG_20200508_1
30758-287x300.jpg" alt="" width="90" height="300" />.
</div>
</div>
</a>
You're probably clicking the img, not the div that the image is in, so event.target is the img.
To find the nearest ancestor (starting with the current element) that matches a selector, use closest.
You're also using myFunction both in onclick="myFunction(this,event)" and in an addEventListener call. Those will provide different arguments to the function. Remove the onclick and just use addEventListener.
Here's the updated function:
function myFunction(x) {
const timage = x.target.closest(".timage");
if (timage && this.contains(timage)) {
this.classList.toggle('change');
}
}
The reason for the contains is just completeness and in many cases you can leave it off: It's to defend against closest having gone past the container element and found the match in the container's ancestors. That won't happen with your layout because timage is only used with the container, but for more general situations, I include that check. For instance:
<div class="x">
<div id="container">
<div class="x">xxx</div>
<div class="y">yyy</div>
</div>
</div>
There, if I have click hooked on #container and the user clicks yyy and I'm doing const x = event.target.closest(".x"), I'll get the .x that's the parent of the container. So this.contains(x) weeds those out.
Related
I have the following nested addEventListener to keep track of the dragged element and the element i am dragging to.
var objects = document.querySelector('.objects');
var destination = document.querySelector('.dest');
objects.addEventListener('dragstart', function(e) {
console.log("dragstart")
destination.addEventListener('dragover', function(e2) { e2.preventDefault(); });
destination.addEventListener('drop', function(e2) {
console.log("drop")
console.log(e.target);
console.log(e2.target);
})
})
<style>
div >div {
margin: 50px;
width: 100px;
height: 100px;
background-color: green;
}
</style>
<body>
<div class="objects">
<div draggable='true' id='o1'>A</div>
</div>
<div class="dest">
<div id='D1' >1</div>
</div>
</body>
The first time i am drag and dropping everything seems as i expected, but when i drag and drop a second time, the addEventListener for drop is executed twice, and the third time, three times. Why is that so?
If i only console.log the drag and drop strings, this behavior of doubling and trippling does't show up. What happens with the events e and e2?
You can use removeEventListener to remove the earlier events that were added so that it doesn't get re-added every time it's dragged, in order to do that you just have to store the function separately though
var objects = document.querySelector('.objects');
var destination = document.querySelector('.dest');
function dragFnc(e2) {
e2.preventDefault();
}
function dropt(e2) {
console.log("drop")
console.log(e2.target);
e2.target.removeEventListener("dragover",dropt)
e2.target.removeEventListener("drop",dragFnc)
}
objects.addEventListener('dragstart', function(e) {
console.log("dragstart")
destination.addEventListener('dragover', dragFnc);
destination.addEventListener('drop', dropt)
})
<style>
div >div {
margin: 50px;
width: 100px;
height: 100px;
background-color: green;
}
</style>
<body>
<div class="objects">
<div draggable='true' id='o1'>A</div>
</div>
<div class="dest">
<div id='D1' >1</div>
</div>
</body>
I want to be able to click on a div, but not in the area of another div inside a div inside the outer div.
I tried to select my other div without the inner div using :not() in the selector, but it didn't work.
<div class=outer>
<div class=inner1>
<div class=inner2>
<div class=notClickable>
<div class=alsoNotClickable>
...
</div>
</div>
</div>
</div>
</div>
$("div.outer:not(div.notClickable)").click(function(){
...
});
I expect that I can click inside div class=outer, but not inside div class=notClickable and its childs.
One option is adding stopPropagation() on the non clickable divs.
The stopPropagation() method of the Event interface prevents further
propagation of the current event in the capturing and bubbling phases.
$("div.outer").click(function() {
console.log("click");
});
$("div.notClickable").click(function(e) {
e.stopPropagation();
});
$("div.alsoNotClickable").click(function(e) {
e.stopPropagation();
});
.outer {
width: 300px;
height: 300px;
background-color: green;
}
.notClickable {
width: 100px;
height: 100px;
background-color: red;
}
.alsoNotClickable {
width: 50px;
height: 50px;
background-color: pink;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class=outer>
<div class=inner1>
<div class=inner2>
<div class=notClickable>
<div class=alsoNotClickable>
</div>
</div>
</div>
</div>
</div>
To achieve this you can check the target property of the event. If it matches the element you hooked the event to then you know that the event has not bubbled up from a child. Try this:
$("div.outer").click(function(e) {
if ($(e.target).is('div.outer')) {
console.log('You clicked the outer div');
} else {
console.log('You clicked a child div');
}
});
div.outer,
div.outer div {
padding: 10px;
border: 1px solid #CCC;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="outer">
Outer
<div class="inner1">
Child
<div class="inner2">
Child
<div class="notClickable">
Child
<div class="alsoNotClickable">
Child
</div>
</div>
</div>
</div>
</div>
I think you can stop the propagation by adding this line to your function:
event.stopPropagation();
Eg:
$("div.outer:not(div.notClickable)").click(function(event) {
...
event.stopPropagation();
});
Please check this fiddle where i use the click function. Let me know if that would fits you :)
Assign an ID to the div you want to select. Then select the ID.
I have a set of div elements inside a container, .div-to-hide is displayed by default whilst .div-to-show is hidden.
When I click in .set, .div-to-hide should hide and .div-to-show should be visible. Next click should return the previous clicked element to its default state.
I need to display to buttons on click inside on .div-to-show.
<div class="container">
<div class="set">
<div class="div-to-hide">Some text</div>
<div class="div-to-show"></div>
</div>
<div class="set">
<div class="div-to-hide">Some text</div>
<div class="div-to-show"></div>
</div>
<div class="set">
<div class="div-to-hide">Some text</div>
<div class="div-to-show"></div>
</div>
</div>
So far I have this:
let lastClicked;
$('.container').on('click', function(e) {
if (this == lastClicked) {
lastClicked = '';
$('.div-to-hide').show();
$(this).children('.div-to-hide').hide();
} else {
lastClicked = this;
$('.div-to-hide').hide();
$(this).children('.div-to-hide').show();
$(this).children('.div-to-show').hide();
}
});
Can't get it to work properly tho.. I don't know what I am missing...
Any help is deeply appreciated!
UPDATE: got it working! Thanks everyone!
First, you are not using delegation (second parameter on the $.on() function) to define the .set element as your this inside the function.
If I understood correctly, you want to show the elements on the last one clicked and hide the rest. You don't really need to know which one you last clicked to do that
$('.container').on('click', '.set', function (e) {
// Now "this" is the clicked .set element
var $this = $(this);
// We'll get the children of .set we want to manipulate
var $div_to_hide = $this.find(".div-to-hide");
var $div_to_show = $this.find(".div-to-show");
// If it's already visible, there's no need to do anything
if ($div_to_show.is(":visible")) {
$div_to_hide.show();
$div_to_show.hide();
}
// Now we get the other .sets
var $other_sets = $this.siblings(".set");
// This second way works for more complex hierarchies. Uncomment if you need it
// var $other_sets = $this.closest(".container").find(".set").not(this);
// We reset ALL af them
$other_sets.find(".div-to-show").hide();
$other_sets.find(".div-to-hide").show();
});
Consider using class toggling instead.
$('.set').on('click', function(e) {
$('.set').removeClass('hidden-child');
$(this).addClass('hidden-child');
});
css:
.hidden-child .div-to-hide, .div-to-show {
display: none;
}
.hidden-child .div-to-show, .div-to-hide {
display: block;
}
This will make your code easier to reason about, and lets css control the display (style) rules.
Edit: changed class name for clarity; expanded explanation; corrected answer to conform to question
Try to make use of siblings() jQuery to hide and show other divs and toggle() jQuery to show and hide itself and also you will need to set click() event on .set, not in .container
$(document).on('click', '.set', function(e) {
$(this).find('.hide').toggle();
$(this).find('.show').toggle();
$(this).siblings('.set').find('.hide').show();
$(this).siblings('.set').find('.show').hide();
});
.show {
display: none;
}
.set div {
padding: 10px;
font: 13px Verdana;
font-weight: bold;
background: red;
color: #ffffff;
margin-bottom: 10px;
cursor: pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<div class="set">
<div class="hide">1 Hide</div>
<div class="show">1 Show</div>
</div>
<div class="set">
<div class="hide">2 Hide</div>
<div class="show">2 Show</div>
</div>
<div class="set">
<div class="hide">3 Hide</div>
<div class="show">3 Show</div>
</div>
</div>
I want my divs to change colour upon hovering over them, but the code is not executing even when I'm hovering. I'm not completely sure why, but I think there could possibly be an issue with the fact that I'm using a z-index on the class I want to hover over.
Html with script:
$(".eventContents").hover(
function() {
$(".eventContents").css("background-color", "yellow");
})
//making events square
var cw = $('.eventContain').width();
$('.eventContain').css({
'height': cw + 'px'
});
.eventContain {
position: relative;
margin-bottom: 10px;
z-index: -1;
background-size: cover;
}
.eventContents {
color: white;
padding: 5px;
position: absolute;
bottom: 0;
left: 0;
}
.eventContents h2 {
font-size: 2em;
font-family: 'Montserrat', sans-serif;
}
.eventContents p {
font-size: 1em;
font-family: 'Roboto', sans-serif;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<section id="events">
<row>
<div class="col-sm-4">
<div class="eventContain" style="background-image:url(img/events/leaf.jpg)">
<div class="eventContents">
<h2 class="eventName">Title of Event</h2>
<p>short description goes about here.</p>
</div>
</div>
</div>
<div class="col-sm-4">
<div class="eventContain" style="background-image:url(img/events/12.jpg)">
<div class="eventContents">
<h2 class="eventName">Title of Event</h2>
<p>short description goes about here.</p>
</div>
</div>
</div>
<div class="col-sm-4">
<div class="eventContain" style="background-image:url(img/events/1.jpg)">
<div class="eventContents">
<h2 class="eventName">Title of Event</h2>
<p>short description goes about here.</p>
</div>
</div>
</div>
</row>
</section>
Here is the fiddle, the issue is more prominent here:
https://jsfiddle.net/jakexia72/x7jLp17z/#&togetherjs=os0pjD0RNr
It seems to work for me, if I understood correctly, but here's a way to hover both on and off and use this instead of .eventContents twice more..
$('.eventContents').hover(
function() {
$(this).css('background-color', 'yellow');
},
function() {
$(this).css('background-color', 'red');
}
);
fiddle
https://jsfiddle.net/Hastig/4fjn0ndb/1/
The elements are being correctly hovered and the code is getting executed I've tested it, the problem is maybe that your elements are position:absolute; and they're all in top of each other, also they don't have a defined height and it's necessary because we are talking about div elements not img, maybe you'd want to check out your code a little bit better.
You'll want to put a top:0px; to your .eventContents because it's hidden on top (at least for this example)
One last thing, if you want to refer to the actual hovered element, you should use $(this) instead of the class name because it'll execute the code for all the elements with the class and not only the hovered one.
The negative z-index is the reason why the hover is not working, to fix it, make sure that the z-index of the element you want to hover over is positive. To avoid affecting the top nav bar, move the nav bar to the bottom of the html code file allowing it to naturally appear on top of everything else, avoiding the need to use a negative z-index on eventContain.
I would use one of the readily available jQuery plugins, but none of them fit my needs for this particular site.
This is the code that I have so far:
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Untitled Document</title>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.js"></script>
<script type="text/javascript" src="js/pushState.js"></script>
<script type="text/javascript">
$(document).ready(function() {
$("#nav a.slide").click(function() {
var img = $(this).children('img').attr('src');
$('#content').hide().css({ 'background':'url('+ img +') no-repeat 50% 50%' }).fadeIn('3000');
var remApp = $(this).prev('a.slide');
remApp.remove();
$("#nav").append(remApp);
});
});
</script>
<style type="text/css">
html, body {
padding: 0px;
margin: 0px;
}
#content {
position: absolute;
width: 100%;
height: 100%;
padding: 0px;
margin: 0px;
z-index: 1;
}
#nav {
position: absolute;
top: 70%;
z-index: 3;
}
#nav a.slide {
margin: 0 20px 0 0;
text-decoration: none;
}
#nav .slide img {
width: 100px;
height: 85px;
}
</style>
</head>
<body>
<div id="content">
</div>
<div id="social_links">
</div>
<div id="nav">
<a href="#" class="slide">
<img src="images/slide1.gif" />
</a>
<a href="#" class="slide">
<img src="images/slide2.jpg" />
</a>
<a href="#" class="slide">
<img src="images/slide3.jpg" />
</a>
<a href="#" class="slide">
<img src="images/slide4.jpg" />
</a>
<a href="#" class="slide">
<img src="images/slide5.png" />
</a>
</div>
</body>
</html>
This takes the image that you click on and removes it, then appends it to the end. I need it to take each of the previous images and append them.
Also, it seems to have a one-time rule. I can make each of the images go once, but then nothing happens.
You can see it in "action" here
There are two possible issues (solving one of them should help) resulting from the fact, that the events are bound to the elements, and when the elements are removed and added again, the events are not bound to the elements any more. In other words you remove the element from the DOM along with deleting the events attached, but when you insert them in the DOM again, the events are not re-attached.
There are two ways you can fix it:
do not attach events to the elements, but to the container, and then delegate the events (using jQuery's .delegate() function) to the elements you need, eg. like that:
$("#nav").delegate('a.slide', 'click', function() {
var img = $(this).children('img').attr('src');
$('#content').hide().css({ 'background':'url('+ img +') no-repeat 50% 50%' }).fadeIn('3000');
var remApp = $(this).prev('a.slide');
remApp.remove();
$("#nav").append(remApp);
});
when removing the elements, do it using .detach(), not .remove() jQuery's function - it will remove the elements without deleting the events. As .remove()'s documentation says:
Similar to .empty(), the .remove() method takes elements out of the
DOM. Use .remove() when you want to remove the element itself, as well
as everything inside it. In addition to the elements themselves, all
bound events and jQuery data associated with the elements are removed.
To remove the elements without removing data and events, use .detach()
instead.
Your event handlers are being removed from the slides when you use .remove. Per the jQuery documentation (emphasis mine):
Use .remove() when you want to remove the element itself, as well as everything inside it. In addition to the elements themselves, all bound events and jQuery data associated with the elements are removed. To remove the elements without removing data and events, use .detach() instead.
I wouldn't remove it or detatch it though. Just append it and jQuery will automatically put it at the end. Since you want to move all previous siblings to the end too, you want this:
var remApp = $(this).prev('a.slide');
$("#nav").append(remApp.prevAll().andSelf());
Note that I removed the .remove command and I added .prevAll().andSelf() to remApp. .prevAll() gets all previous siblings (but not remApp) and .andSelf() rejoins remApp to the collection. Then you append the whole collection to the end of the list.