I have an item like the image below
1: click to open detail page
2: click to switch item status (true/false) and stay on this page
Element 2 position: absolute and above element 1
When I click on element 2, click event firing on element 1 and the page redirect to detail page, no event firing for element 2.
Here is my design and code behind:
<div class="investment-content_image" ng-click="open(item.id)">
<div class="closed-overlay-fra">
<img class="closed-photo" ng-src="{{item.getClosedImage()}}" />
</div>
<div class="investment-content-closed" ng-if="!item.open" ng-class="{'active': hovering}" ng-click="open(item.id)">
<span class="investment-content-closed-text">SOLD OUT</span>
</div>
<label class="toggle-switch" ng-if="user.isAdvisor()" ng-click="updateHideInvestment()">
<input type="checkbox" ng-model="item.hide_investor">
<div class="switch-slider">
<span class="glyphicon glyphicon-ok"></span>
<span class="glyphicon glyphicon-remove"></span>
</div>
</label>
</div>
$scope.open = function (id) {
if (!$scope.user) {
return;
}
if ($scope.user.isAdmin()) {
$state.go('admin.showInvestment.overview', {investmentId: id});
} else if ($scope.user.isInvestor()) {
$state.go('investor.showInvestment.overview', {investmentId: id});
} else if ($scope.user.isAdvisor()) {
$state.go('advisor.showInvestment.overview', {investmentId: id});
}
};
$scope.updateHideInvestment = function () {
let data = {
id: $scope.item.id,
hide: $scope.item.hide_investor
};
advisorsSvc.updateHideInvestment(data)
.then((result) => {
$scope.item.hide_investor = result.hide_investor;
})
.catch((err) => { throw err; });
}
ng-click will listen to all click events inside the element and all children elements.
In order to prevent the outer layer from getting the click, you'll need to stop the click from propagating up.
ng-click="$event.stopPropagation(); open(item.id)"
Can you provide css also? Here simplified version using JQuery
HTML:
<div id="first"></div>
<div id="second"></div>
CSS:
#first
{
position:absolute;
background-color:red;
width:300px;
height:300px;
}
#second
{
position:absolute;
margin-left:200px;
margin-top:200px;
background-color:green;
width:50px;
height:50px;
}
JavaScript:
$("#first").click(function()
{
alert("first");
});
$("#second").click(function()
{
alert("second");
});
https://jsfiddle.net/maximelian/mkgmL83r/
This could be an issue related to Event Bubbling. When an event is triggered on an element, all of its parent elements will fire this event, too. The event is bubbling up parent after parent. You need to stop the propagation of the event on the lowest level.
For this you have to get the generated click event by letting angular pass it to your function.
<label class="toggle-switch" ng-if="user.isAdvisor()" ng-click="updateHideInvestment($event)">
In the function itself, you have to take the event and stop its propagation.
$scope.updateHideInvestment = function (event) {
event.stopPropagation()
// Rest of your code here
}
This will prevent the event from bubbling up and thus prevent your openfunction from being triggered.
Related
I'm using the hash to detect the current slide in a slideshow but I'd like to only do so when the slideshow is advanced using the previous or next buttons. But the event "cycle-after" which detects a transition in the slideshow, is firing even when the previous or next buttons are not clicked.
How do I make that event only run during the click function?
JSFiddle here: https://jsfiddle.net/yd8L3enj/4/
$(document).ready(function() {
var clicked = false;
$('.controls').on('click', function() {
$('.cycle-slideshow').on('cycle-after', function(event, optionHash) {
var hash = window.location.hash;
$('.clicked').removeClass('clicked')
if (window.location.hash === hash) {
$(hash).addClass('clicked')
} else {
$(hash).removeClass('clicked')
}
});
});
$('nav a').on('click', function() {
clicked = !clicked;
$('.clicked').removeClass('clicked');
$(this).addClass('clicked');
$('.content').addClass('visible');
});
$("nav a").mouseenter(function() {
var href = $(this).attr('href');
window.location.hash = href
$('.content').addClass('visible');
}).mouseleave(function() {
var current = $('.clicked').attr('href');
window.location.hash = current
if ($(".clicked")[0]) {
// Do something if class exists
} else {
$('.content').removeClass('visible');
}
});
$('.close').on('click', function() {
$('.content').removeClass('visible');
window.location.hash = ""
clicked = !clicked;
});
});
body {
font-size: 150%;
}
img {
width: 50vw;
height: auto;
}
.clicked {
color: green;
}
.content {
display: none;
}
.visible {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.cycle2/2.1.6/jquery.cycle2.min.js"></script>
<nav>
1
2
3
</nav>
<div class="content">
<div class="cycle-slideshow" data-cycle-slides="> div" data-cycle-timeout="0" data-cycle-prev=".prev" data-cycle-next=".next" data-cycle-speed="1" data-cycle-fx="fadeOut">
<div data-cycle-hash="1">
<img src="https://placeimg.com/640/480/animals">
</div>
<div data-cycle-hash="1">
<img src="https://placeimg.com/640/480/animals/2">
</div>
<div data-cycle-hash="2">
<img src="https://placeimg.com/640/480/arch">
</div>
<div data-cycle-hash="2">
<img src="https://placeimg.com/640/480/arch/2">
</div>
<div data-cycle-hash="3">
<img src="https://placeimg.com/640/480/nature">
</div>
<div data-cycle-hash="3">
<img src="https://placeimg.com/640/480/nature/2">
</div>
</div>
<div class="controls">
<div class="prev">Prev</div>
<div class="next">Next</div>
<div class="close">Close</div>
</div>
</div>
the $(selector).on(... syntax binds an event listener. Your code is adding an event listener to the 'cycle-after' event every time the click listener is executed. That means, as soon it was clicked once, all cycle-after events from then on will have that code executed. If you clicked multiple times, you will have bound multiple listeners, and even more of them will be running on every cycle-after event.
What you probably want to do is, for a click, only perform the code after the first next cycle-after event. To achieve this you could bind the listener, and at the end of the callback, unbind it again. Something like this:
$('.controls').on('click', function() {
$('.cycle-slideshow').on('cycle-after', afterCycle);
function afterCycle(){
... your logic here ...
$('.cycle-slideshow').off('cycle-after', afterCycle);
}
});
Keep in mind that this is still pretty fragile. If you click twice before the first cycle-after happens, the library might only fire cycle-after once and you will still have an unwanted listener bound. If this slide-library supports it, it would be best to simply bind once on 'cycle-after', and then add a check that only continues if the cycle was caused by a click.
I'm having some trouble figuring out how to close a div by clicking anywhere on the screen.
I'm currently toggling an 'active' class in order to display a drop down div, then attempting to remove that class by clicking on the body:
$(document).ready(function () {
$('.navbar a').click(function () {
$(this).next('.navbar-dropdown').toggleClass('active');
});
$(body).click(function() {
if($('.navbar-dropdown').hasClass('active')){
$('.navbar-dropdown').removeClass('active');
}
});
});
<ul class="navbar">
<li>
Link
<div class="navbar-dropdown">
Dropdown Content
</div>
</li>
</ul>
However they are conflicting with each other, so as soon as the class is toggled on, the 'body' click toggles it off at the same time. Have spent some time looking on here and came across this method a few times:
$(document.body).click( function() {
closeMenu();
});
$(".dialog").click( function(e) {
e.stopPropagation();
});
However any attempts to configure this to work correctly seemed to fall on deaf ears!
The click event from the navbar is bubbling up to the body, so both events fire. stopPropagation() is one way to prevent that, but you need to do it in the navbar link's event handler, so it stops that particular event; not in a separate event handler as you had it.
Another change you might consider making is to only assign the body click handler when you need it, instead of firing all the time -- create that handler inside the navbar's click handler, and deactivate it again when it's used:
$(document).ready(function() {
$('.navbar a').click(function(e) {
var myDropdown = $(this).next('.navbar-dropdown');
$('.navbar-dropdown.active').not(myDropdown).removeClass('active'); // close any other open dropdowns
myDropdown.toggleClass('active'); // open this one
$('body').click(function() {
// no need for an if statement here, just use a selector that matches the active elements:
$('.navbar-dropdown.active').removeClass('active');
$('body').off('click'); // cancel the body's click handler when it's used
});
e.stopPropagation(); // prevent the navbar event from bubbling up to the body
});
});
.active {
color: red
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<ul class="navbar">
<li>
Link
<div class="navbar-dropdown">
Dropdown Content
</div>
</li>
<li>
Link 2
<div class="navbar-dropdown">
Dropdown Content 2
</div>
</li>
<li>
Link 3
<div class="navbar-dropdown">
Dropdown Content 3
</div>
</li>
</ul>
(If there's a chance you might need more than one separate click event handler on the body, you can namespace the event so you can control which one you're turning off:
$('body').on("click.myNamespace", function() {
// do other stuff
$('body').off("click.myNamespace")
})
I did the exact thing as you and it works for me. Are you sure you don't have any other event listeners attached? Or maybe a z-index on the menu bringing it underneath other elements?
$(document).click(function(e) {
$(".dialog").text('closed')
});
$(".dialog").click(function(e) {
e.target.innerText = 'open';
e.stopPropagation();
});
.dialog {
width: 200px;
height: 200px;
background: antiquewhite;
text-align: center;
}
<html>
<head>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<div class="dialog">open</div>
</body>
</html>
I have a div that expands when clicked. I also have a link inside this div. Expected behavior is for the click event to be ignored when the link is clicked, but obviously clicking anywhere in the div will trigger the event. I've tried stopPropagation(), but it doesn't seem to work.
$('#infobox').on('click', expandFunction);
$('#infobox a').on('click', function(event) {
event.stopImmediatePropagation();
});
<div class="infobox">
Link
</div>
function expandFunction() {
alert("expanded")
}
document.getElementById("infobox").addEventListener('click', function (event) {
if (event.target.tagName != event.currentTarget.tagName) return;
expandFunction();
});
document.querySelector("#infobox a").addEventListener('click', function (event) {
alert("linkclicked")
})
div{
padding:15px;
background-color:gray;
}
a{
padding:15px;
background-color:silver;
color:white;
margin:5px;
display:inline-block
}
<div id="infobox" class="infobox">
Link
</div>
first you must set Id for infobox
<div id="infobox" class="infobox">
you can use this code to prevent click from other elements
$('#infobox').on('click', function(event){
if(event.target.id!= event.currentTarget.id) return;
expandFunction();
});
In my code, I have added onclick on parent div and want to perform other action on inner div, but clicking on inner div also triggering parent click.
how to stop that?
$(document).on('click', '.child', function(e) {
e.preventDefault();
e.stopPropagation();
console.log('child');
});
function parentfun(sender) {
console.log('parent');
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="parent" onclick="parentfun(this)">
parent
<div class="child">child</div>
</div>
Above divs are generated on run time on some other event.
Clicking on child, also trigger parent's click. preventDefault & stopPropagation are not working.
FYI: my question is different than How do I prevent a parent's onclick event from firing when a child anchor is clicked?
What you are actually doing here is binding the click-event to the document, not the child-element. So the event has already bubbled up all the way to the document, it's too late to try to stop the bubbling with stopPropagation.
See here when I change the click-handler to the child instead:
$(".child").on('click', function(e) {
e.preventDefault();
e.stopPropagation();
console.log('child');
});
function parentfun(sender) {
console.log('parent');
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="parent" onclick="parentfun(this)">
parent
<div class="child">child</div>
</div>
Edit
As the question changed a bit, here is what you can do (for example) if the elements are created dynamically:
$(document).on('click', '.parent, .child', function(e) {
e.stopPropagation();
if ($(this).is(".child")) {
console.log('child');
} else {
console.log('parent');
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="parent">
parent
<div class="child">child</div>
</div>
Using plain vanilla JS it works as expected:
function logEventTarget(e) {
e.stopPropagation();
console.log(e.target.id);
}
parentDiv.addEventListener('click', logEventTarget)
childDiv.addEventListener('click', logEventTarget)
<div id="parentDiv">
parent
<div id="childDiv">child</div>
</div>
Using an inline event handler won't pass the event to the handler function:
function logEventTarget(e) {
e.stopPropagation();
console.log(e.target.id);
}
childDiv.addEventListener('click', logEventTarget)
<div id="parentDiv" onclick="logEventTarget()">
parent
<div id="childDiv">child</div>
</div>
One of the many reasons you shouldn't use inline event handlers at all. Note that e.stopPropagation() still works for the childDiv.
You can notice that when clicking the chlid element the parent triggers first (that is why parent prints first then child ) because of event capturing which precedes event bubbling. In-order to stop the event capturing phase from parent you can stop propagating that event and then only child event will trigger.
$(document).on('click', '.child', function(e) {
//e.preventDefault();
e.stopPropagation();
console.log('child');
});
$(document).on('click', '.parent', parentfun);
function parentfun(e) {
e.stopPropagation();
console.log('parent');
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="parent">
parent
<div class="child">child</div>
</div>
You can also resolve this problem by editing little bit in Your html code
<div class="parent" id="parent-div">
<!-- Just adding parent div text inside span tag -->
<span>parent</span>
<div class="child">child</div>
</div>
now go to jquery code
$('.parent span').on('click',function(){
//only print child text u can do more
alert($('.child').text());
//Change color of child
$('.child').css('color','red');
});
I'm attempting to track events for all UI elements on a page. The page contains dynamically generated content and various frameworks / libraries. Initially I tracked elements through creating a css class "track" , then adding style "track" to tracked elements. elements are then tracked using :
$('.track').on('click', function() {
console.log('Div clicked' + this.id);
console.log(window.location.href);
console.log(new Date().getTime());
});
As content can be dynamically generated I wanted a method to track these elements also. So tried this using wildcard jQuery operator.
In this fiddle : https://jsfiddle.net/xx68trhg/37/ I'm attempting to track all elements using the jquery '*' selector.
Using jQuery '*' selector appears to fire the event for all elements of given type.
So for this case if is clicked all the click event is fired for all divs. But id is just available for div being clicked.
For the th element the click event is fired twice , what is reason for this ?
Can the source be modified that event is fired for just currently selected event ?
fiddle src :
$(document).ready(function() {
$('*').each(function(i, ele) {
$(this).addClass("tracked");
});
$('.tracked').on('click', function() {
console.log('Div clicked' + this.id);
console.log(window.location.href);
console.log(new Date().getTime());
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<!-- <div id="1" data-track="thisdiv">
Any clicks in here should be tracked
</div>
-->
<div id="1">
Any clicks in here should be tracked 1
</div>
<div id="2">
Any clicks in here should be tracked 2
</div>
<div id="3">
Any clicks in here should be tracked 3
</div>
<th id="th">tester</th>
You can try with:
$(document).ready(function() {
$("body > *").click(function(event) {
console.log(event.target.id);
});
});
$(document).ready(function() {
$("body > *").click(function(event) {
console.log(event.target.id);
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="1">
Any clicks in here should be tracked 1
</div>
<div id="2">
Any clicks in here should be tracked 2
</div>
<div id="3">
Any clicks in here should be tracked 3
</div>
<table>
<tr>
<td>Cols 1</td>
<td id="td">Cols 2</td>
</tr>
</table>
<p id="th">tester</p>
You may want to use event delegation to target the elements you need. Advantage is that this also works for dynamically generated elements. See code for an example of this.
// method to add/set data-attribute and value
const nClicksInit = (element, n = "0") => element.setAttribute("data-nclicked", n);
// add data-attribute to all current divs (see css for usage)
// btw: we can't use the method directly (forEach(nClicksInit))
// because that would send the forEach iterator as the value of parameter n
document.querySelectorAll("div").forEach(elem => nClicksInit(elem));
// add a click handler to the document body. You only need one handler method
// (clickHandling) to handle all click events
document.querySelector('body').addEventListener('click', clickHandling);
function clickHandling(evt) {
// evt.target is the element the event is generated
// from. Now, let's detect what was clicked. If none of the
// conditions hereafter are met, this method does nothing.
const from = evt.target;
if (/^div$/i.test(from.nodeName)) {
// aha, it's a div, let's increment the number of detected
// clicks in data-attribute
nClicksInit(from, +from.getAttribute("data-nclicked") + 1);
}
if (from.id === "addDiv") {
// allright, it's button#addDiv, so add a div element
let newElement = document.createElement("div");
newElement.innerHTML = "My clicks are also tracked ;)";
const otherDivs = document.querySelectorAll("div");
otherDivs[otherDivs.length-1].after(newElement);
nClicksInit(newElement);
}
}
body {
font: 12px/15px normal verdana, arial;
margin: 2em;
}
div {
cursor:pointer;
}
div:hover {
color: red;
}
div:hover:before {
content: '['attr(data-nclicked)' click(s) detected] ';
color: green;
}
#addDiv:hover:after {
content: " and see what happens";
}
<div id="1">
Click me and see if clicks are tracked
</div>
<div id="2">
Click me and see if clicks are tracked
</div>
<div id="3">
Click me and see if clicks are tracked
</div>
<p>
<button id="addDiv">Add a div</button>
</p>
<h3 id="th">No events are tracked here, so clicking doesn't do anything</h3>
You can invoke the stopPropagation and the condition this === e.currentTarget to ensure invoke the handler function of the event source DOM.
And you must know the <th> tag must wrapped by <table>, otherwise it will not be rendered.
$(document).ready(function() {
$('*').each(function(i, ele) {
$(this).addClass("tracked");
});
$('.tracked').on('click', function(e) {
if (this === e.currentTarget) {
e.stopPropagation();
console.log('Div clicked' + this.id);
console.log(window.location.href);
console.log(new Date().getTime());
}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<!-- <div id="1" data-track="thisdiv">
Any clicks in here should be tracked
</div>
-->
<div id="1">
Any clicks in here should be tracked 1
</div>
<div id="2">
Any clicks in here should be tracked 2
</div>
<div id="3">
Any clicks in here should be tracked 3
</div>
<table>
<th id="th">tester</th>
</table>