Event inside click function firing outside click - javascript

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.

Related

Use jQuery to click anywhere and remove 'active' class

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>

Track all elements on page

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>

Click event not fire for the second element above another div

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.

Making a number increment when a class is clicked

So right now I have this code:
var s = 0;
$('.inner').click(function () {
$(this).addClass("selected");
$(this).removeClass("inner");
s++;
$('#sslots').replaceWith(s);
};
But for some reason, the javascript wont update, it will start out as blank (not zero) and then change to 1 once I click one of the div's with "inner" as the class but then won't do anything after that..
The problem is after your first click the element sslots does not exists because you are replacing it with the number, instead you have to change the content of sslots - you can use .text() for that
var s = 0;
$('.inner').one('click', function() {
$(this).addClass("selected");
$(this).removeClass("inner");
s++;
$('#sslots').text(s);
});
.inner {
color: green;
}
.selected {
color: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="sslots">0</div>
<div class="inner">inner</div>
<div class="inner">inner</div>
<div class="inner">inner</div>
<div class="inner">inner</div>
<div class="inner">inner</div>
Also from the code it looks like you want to execute the click once per inner element(ie if you click multiple times in an element only first one should count), in that cause use .one() to register a handler which will be executed only once

jquery hide div when click outside

I am trying to style div and ul to function like . However, I have a problem that:
1) I only want to toggle the ul that I click and hide the other ul. So I wonder if jquery support some function such as 'not click'?
2) I want to hide all the ul when the mouse is click outside. I did some research, and see other people use mouseup or click on body. But I am not quiet sure how it works.
$(document).ready(function() {
$('.hide').each(function() {
$(this).hide();
});
$('.select').click(function() {
var id = '#' + $(this).attr('id');
var sub = id + '_sub';
$(sub).slideToggle();
});
$('body').mouseup(function() {
if($(this).length == 0) {
$(this).hide();
}
});
});
div.select {
display: inline-block;
margin: 10px;
padding: 20px;
background: red;
cursor: pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<body>
<div id="1" class="select">
<div class="main">
<span>1</span>
</div>
<div>
<ul id="1_sub" class="hide">
<li>1</li>
<li>2</li>
</ul>
</div>
</div>
<div id="2" class="select">
<div class="main">
<span>1</span>
</div>
<div>
<ul id="2_sub" class="hide">
<li>1</li>
<li>2</li>
</ul>
</div>
</div>
<div id="3" class="select">
<div class="main">
<span>1</span>
</div>
<div>
<ul id="3_sub" class="hide">
<li>1</li>
<li>2</li>
</ul>
</div>
</div>
</body>
here you go: DEMO
$(document).ready(function() {
$('.hide').hide(); //hide in the beginning
$('.select').click(function() {
$('.hide').slideUp(200); //hide all the divs
$(this).find('.hide').slideDown(200); //show the one that is clicked
});
$(document).click(function(e){
if(!$('.select').is(e.target) || !$('.select').has(e.target)){ // check if the click is inside a div or outside
$('.hide').slideUp(200); // if it is outside then hide all of them
}
});
});
you can define your notClick() function as below:
$.fn.notClicked= function(clickPosition){
if (!$(this).is(clickPosition.target) && $(this).has(clickPosition.target).length === 0){
return true;
}
else{
return false;
}
};
and then use it as:
$(document).click(function(e){
alert($('.select').notClick(e)); // will return true if it is not clicked, and false if clicked
});
You need to hide other ul whenever some one clicks on .select div.
Here is a working fiddle: http://jsfiddle.net/0mgbsa0b/1/
$(document).ready(function() {
$('.hide').each(function() {
$(this).hide();
});
$('.select').click(function() {
$('.hide').each(function() {
$(this).hide();
});
var id = '#' + $(this).attr('id');
var sub = id + '_sub';
$(sub).slideToggle();
});
$('body').mouseup(function() {
if($(this).length == 0) {
$(this).hide();
}
});
});
I'm interested in two concerns you raised, so i will be trying to share some ideas on them:
1)So I wonder if jquery support some function such as 'not click'?
personally, to quesiton1
i think there is no jQuery event method called .noclick()
PPL often use addClass & removeClass to log whether an element got clicked and after marking the element with class="active" , using jQuery selector to select ".active" or using jQuery ":not" selector to select elements that are not marked ".active" ( indirectly finding out those unclicked.)
3.You might also need to count in click propagation issues. meaning sometimes you click a children container and triggered click event towards all its parent inside.
fiddle link: `http://jsfiddle.net/hahatey/ctp5jngf/2/`
In the above case , if you clicked child box in red, will by default alert1, alert2 if
you didn't apply a e.stopPropagation() to the click event;
2) I want to hide all the ul when the mouse is click outside. I did some research, and see other people use mouseup or click on body. But I am not quiet sure how it works.
for question 2:
could be many many ways to do it, you can try blur() //lose focus event trigger.
like what you mentioned mouseout, mouseup, add click event listener to outer area all will work for it as long as u can use method in answer1. i see other ppl have posted many answers already as it can be done in many ways.

Categories