How To prevent openContent(); to kick the $("#load-content").on("transitionend each time I click .show-content ???
I'm not sure how to stop this transitionend to be kicked! Please heeeelp
$('.show-content').on('click', function (e) {
e.preventDefault();
openContent();
});
$('#load-content').on('click','.prev',function (e){
e.preventDefault();
closeContent(this);
});
function openContent(){
$('#load-content').load('../views/product-page.html');
$('.container').addClass('loaded');
$("#load-content").on("transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd", function () {
$(this).addClass('animate');
var body = $("body,html");
body.animate({
scrollTop: 0
}, 800);
});
}
function closeContent(ele){
var Loaded = !$(ele).closest('.container').hasClass('loaded');
if (!Loaded) {
$('.animate').removeClass('animate');
$("#load-content").on("transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd", function () {
$('.loaded').removeClass('loaded');
$('#show-content').remove();
});
}
}
generally you should namespace the event and the off the event after being fired
$el.on('transitionend.mynamespace' function(){
$el.off('transitionend.mynamespace')
});
Example:
$dropdown.on('transitionend.fadein' function() {
// some function to be called on transitionend
doSomething();
// event will not be called again
$dropdown.off('transitionend.fadein')
});
UPDATE
adapted to your code
(you are also using way too many transitionend hendlers)
I created a namespace with e subnamespace
so now you can say
.off('transitionend.loadcontent ')
.off('transitionend.loadcontent.open ')
.off('transitionend.loadcontent.close ')
Try which one will do what you need
You should generallly read this: http://api.jquery.com/event.namespace/
Also the code doesn't look too amazing.
You should consider a more consequent codingstyle and cache selectors to improve readability and performance. E.g. I replaced all " with ' since you were using mixed quotes.
Maybe run jsHint in your editor and cache all elements that are needed more than once.
But that's not really important for this thing to work.
$('.show-content').on('click', function(e) {
e.preventDefault();
openContent();
});
$('#load-content').on('click', '.prev', function(e) {
e.preventDefault();
closeContent(this);
});
function openContent() {
$('#load-content').load('../views/product-page.html');
$('.container').addClass('loaded');
$('#load-content').on('transitionend.loadcontent.open webkitTransitionEnd.loadcontent.open', function() {
$(this).addClass('animate');
var body = $('body,html');
body.animate({
scrollTop: 0
}, 800);
$('#load-content').off('transitionend.loadcontent.open webkitTransitionEnd.loadcontent.open');
});
}
function closeContent(ele) {
var Loaded = !$(ele).closest('.container').hasClass('loaded');
if (!Loaded) {
$('.animate').removeClass('animate');
$('#load-content').on('transitionend.loadcontent.close webkitTransitionEnd.loadcontent.close', function() {
$('.loaded').removeClass('loaded');
$('#show-content').remove();
});
$('#load-content').off('transitionend.loadcontent.close webkitTransitionEnd.loadcontent.close');
}
}
you might need
$ele.one('click',function(){...})
which allows you to bind the event only one time. after being fired, this event listener will unbind itself. check document here:https://api.jquery.com/one/
Related
Does anyone know if there's a way to preventDefault(), but on a timer, so default actions are restored after a certain time?
Here's what I have so far:
function setResetInterval(bool){
var el = $('article');
if(bool){
timer = setInterval(function(){
setTimeout(function(){
console.log('default prevented');
e.preventDefault();
}, 500);
},1000);
}else{
clearInterval(timer);
}
}
if(object.touch.touch){
object.header.menu_button.attr('href',null);
object.touch.articles = $('article');
object.content_blocks.on('click','article',{},function(e){
object.touch.articles.removeClass('on');
$(this).addClass('on');
e.stopPropagation();
setResetInterval(true);
setTimeout(
function() { setResetInterval(false); }, 500);
});
}
Problem is, the function is called after the clickthrough and the action is not prevented. The alternative is the prevent the default action on click, which stop scrolling on mobile devices.
Thinking about it more clearly, the real problem is the click tag in question is basically the entire screen width on mobile.
To build on what Cayce said, one way to approach this is to tie the functionality to a class you later remove.
Demo Fiddle:
In the example, the default will be prevented as long as the div has the .red class, the setTimeout will remove the class after 3 seconds.
JS:
$('body').on('click', '.red', function (e) {
e.preventDefault();
console.log('I only show up while default is prevented');
});
$('body').on('click', 'div', function () {
console.log('I will always show up');
});
setTimeout(function () {
$('div').removeClass('red');
},3000);
I have this simple mouseenter : mouseleave action:
call = $('.js-call');
call.on({
mouseenter: function(e){
// animation
e.stopPropagation();
},
mouseleave: function(e){
// animation
}
});
In this action i have two CSS animations, which have a duration of 300ms. How can I prevent mouseover event for animation end, and fire it again if i'm properly on call element. When i moving fast on my call element action call many times. How to prevent it? Thx for help.
I would go with placing timeouts on both events, and activate the animation only if at the end of that timeout you still meet a condition. Something like that:
var timeoutIn, timeoutOut, delay = 300;
$element.hover(
function() {
if (timeoutOut){
clearTimeout(timeoutOut);
}
timeoutIn = setTimeout(function() {
// ##################
// 'MOUSEENTER' STUFF GOES HERE
// ##################
}, delay);
},
function() {
if (timeoutIn){
clearTimeout(timeoutIn);
}
timeoutOut = setTimeout(function() {
// ##################
// 'MOUSELEAVE' STUFF GOES HERE
// ##################
}, delay);
}
);
Update: I've just created a jQuery plugin called jQuery.hoverDelay.js, you can check it out here: jQuery hoverDelay.js
what you could do is use .bind( eventType [, eventData ], handler(eventObject) ).
bind the event mouseleave after you're done with the animation and vice versa. this should fix it.
Using jQuery .stop() is the correct approach.
Heres a working fiddle starting and stoping an animation with mouseenter and mousleave, without starting new animation if theres already running one.
http://jsfiddle.net/CZtLe/
function animateMe(element, color) {
element.stop(true).animate({
backgroundColor: color,
duration: 300
});
}
$(function () {
call = $('.js-call');
call.on({
mouseenter: function (e) {
animateMe($(this), '#FF0000');
e.stopPropagation();
},
mouseout: function (e) {
animateMe($(this), '#000000');
}
});
});
I have a draggable <div> with a click event and without any event for drag,
but after I drag <div> the click event is apply to <div>.
How can prevent of click event after drag?
$(function(){
$('div').bind('click', function(){
$(this).toggleClass('orange');
});
$('div').draggable();
});
http://jsfiddle.net/prince4prodigy/aG72R/
FIRST attach the draggable event, THEN the click event:
$(function(){
$('div').draggable();
$('div').click(function(){
$(this).toggleClass('orange');
});
});
Try it here:
http://jsfiddle.net/aG72R/55/
With an ES6 class (No jQuery)
To achieve this in javascript without the help of jQuery you can add and remove an event handler.
First create functions that will be added and removed form event listeners
flagged () {
this.isScrolled = true;
}
and this to stop all events on an event
preventClick (event) {
event.preventDefault();
event.stopImmediatePropagation();
}
Then add the flag when the mousedown and mousemove events are triggered one after the other.
element.addEventListener('mousedown', () => {
element.addEventListener('mousemove', flagged);
});
Remember to remove this on a mouse up so we don't get a huge stack of events repeated on this element.
element.addEventListener('mouseup', () => {
element.removeEventListener('mousemove', flagged);
});
Finally inside the mouseup event on our element we can use the flag logic to add and remove the click.
element.addEventListener('mouseup', (e) => {
if (this.isScrolled) {
e.target.addEventListener('click', preventClick);
} else {
e.target.removeEventListener('click', preventClick);
}
this.isScrolled = false;
element.removeEventListener('mousemove', flagged);
});
In the above example above I am targeting the real target that is clicked, so if this were a slider I would be targeting the image and not the main gallery element. to target the main element just change the add/remove event listeners like this.
element.addEventListener('mouseup', (e) => {
if (this.isScrolled) {
element.addEventListener('click', preventClick);
} else {
element.removeEventListener('click', preventClick);
}
this.isScrolled = false;
element.removeEventListener('mousemove', flagged);
});
Conclusion
By setting anonymous functions to const we don't have to bind them. Also this way they kind of have a "handle" allowing s to remove the specific function from the event instead of the entire set of functions on the event.
I made a solution with data and setTimeout. Maybe better than helper classes.
<div id="dragbox"></div>
and
$(function(){
$('#dragbox').bind('click', function(){
if($(this).data('dragging')) return;
$(this).toggleClass('orange');
});
$('#dragbox').draggable({
start: function(event, ui){
$(this).data('dragging', true);
},
stop: function(event, ui){
setTimeout(function(){
$(event.target).data('dragging', false);
}, 1);
}
});
});
Check the fiddle.
This should work:
$(function(){
$('div').draggable({
start: function(event, ui) {
$(this).addClass('noclick');
}
});
$('div').click(function(event) {
if ($(this).hasClass('noclick')) {
$(this).removeClass('noclick');
}
else {
$(this).toggleClass('orange');
}
});
});
DEMO
You can do it without jQuery UI draggable. Just using common 'click' and 'dragstart' events:
$('div').on('dragstart', function (e) {
e.preventDefault();
$(this).data('dragging', true);
}).on('click', function (e) {
if ($(this).data('dragging')) {
e.preventDefault();
$(this).data('dragging', false);
}
});
You can just check for jQuery UI's ui-draggable-dragging class on the draggable. If it's there, don't continue the click event, else, do. jQuery UI handles the setting and removal of this class, so you don't have to. :)
Code:
$(function(){
$('div').bind('click', function(){
if( $(this).hasClass('ui-draggable-dragging') ) { return false; }
$(this).toggleClass('orange');
});
$('div').draggable();
});
With React
This code is for React users, checked the draggedRef when mouse up.
I didn`t use click event. The click event checked by the mouse up event.
const draggedRef = useRef(false);
...
<button
type="button"
onMouseDown={() => (draggedRef.current = false)}
onMouseMove={() => (draggedRef.current = true)}
onMouseUp={() => {
if (draggedRef.current) return;
setLayerOpened(!layerOpened);
}}
>
BTN
</button>
I had the same problem (tho with p5.js) and I solved it by having a global lastDraggedAt variable, which was updated when the drag event ran. In the click event, I just checked if the last drag was less than 0.1 seconds ago.
function mouseDragged() {
// other code
lastDraggedAt = Date.now();
}
function mouseClicked() {
if (Date.now() - lastDraggedAt < 100)
return; // its just firing due to a drag so ignore
// other code
}
I am using mousedown and mouseup to trigger a setInterval function like so:
$("#rotateRight").mousedown(function() {
intervalIRight = setInterval(rotateRight, 0);
}).mouseup(function() {
clearInterval(intervalIRight);
});
This works great, however, if I release the mouse (mouseup) when I am not hovering over $("#rotateRight") then there is technically no 'mouseup' so the interval goes on forever and is never cleared.
I cannot use hover because I want the even to be when a user clicks and holds a mouse. But at the same time, I need to fix this 'mouseup bug.' Any ideas?
UPDATE: New code is as follows, but still does not clear interval because mouseup happens on an iframe, not the DOM.
var intervalIRight;
var intervalILeft;
$("#rotateRight").on('mousedown', function() {
intervalIRight = setInterval(rotateRight, 0);
});
$("#rotateLeft").on('mousedown', function() {
intervalILeft = setInterval(rotateLeft, 0);
});
$(document).on('mouseup', function() {
clearInterval(intervalIRight);
clearInterval(intervalILeft);
});
clear the interval on any mouseup by attaching the event handler to the document.
var intervalIRight;
$("#rotateRight").on('mousedown', function() {
intervalIRight = setInterval(rotateRight, 0);
});
$(document).on('mouseup', function() {
clearInterval(intervalIRight);
});
FIDDLE
I'd like to fade out an element (transitioning its opacity to 0) and then when finished remove the element from the DOM.
In jQuery this is straight forward since you can specify the "Remove" to happen after an animation completes. But if I wish to animate using CSS3 transitions is there anyway to know when the transition/animation has completed?
For transitions you can use the following to detect the end of a transition via jQuery:
$("#someSelector").bind("transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd", function(){ ... });
Mozilla has an excellent reference:
https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Transitions/Using_CSS_transitions#Detecting_the_start_and_completion_of_a_transition
For animations it's very similar:
$("#someSelector").bind("animationend webkitAnimationEnd oAnimationEnd MSAnimationEnd", function(){ ... });
Note that you can pass all of the browser prefixed event strings into the bind() method simultaneously to support the event firing on all browsers that support it.
Update:
Per the comment left by Duck: you use jQuery's .one() method to ensure the handler only fires once. For example:
$("#someSelector").one("transitionend webkitTransitionEnd oTransitionEnd MSTransitionEnd", function(){ ... });
$("#someSelector").one("animationend webkitAnimationEnd oAnimationEnd MSAnimationEnd", function(){ ... });
Update 2:
jQuery bind() method has been deprecated, and on() method is preferred as of jQuery 1.7. bind()
You can also use off() method on the callback function to ensure it will be fired only once. Here is an example which is equivalent to using one() method:
$("#someSelector")
.on("animationend webkitAnimationEnd oAnimationEnd MSAnimationEnd",
function(e){
// do something here
$(this).off(e);
});
References:
.off()
.one()
There is an animationend Event that can be observed see documentation here,
also for css transition animations you could use the transitionend event
There is no need for additional libraries these all work with vanilla JS
document.getElementById("myDIV").addEventListener("transitionend", myEndFunction);
function myEndFunction() {
this.innerHTML = "transition event ended";
}
#myDIV {transition: top 2s; position: relative; top: 0;}
div {background: #ede;cursor: pointer;padding: 20px;}
<div id="myDIV" onclick="this.style.top = '55px';">Click me to start animation.</div>
Another option would be to use the jQuery Transit Framework to handle your CSS3 transitions. The transitions/effects perform well on mobile devices and you don't have to add a single line of messy CSS3 transitions in your CSS file in order to do the animation effects.
Here is an example that will transition an element's opacity to 0 when you click on it and will be removed once the transition is complete:
$("#element").click( function () {
$('#element').transition({ opacity: 0 }, function () { $(this).remove(); });
});
JS Fiddle Demo
For anyone that this might be handy for, here is a jQuery dependent function I had success with for applying a CSS animation via a CSS class, then getting a callback from afterwards. It may not work perfectly since I had it being used in a Backbone.js App, but maybe useful.
var cssAnimate = function(cssClass, callback) {
var self = this;
// Checks if correct animation has ended
var setAnimationListener = function() {
self.one(
"webkitAnimationEnd oanimationend msAnimationEnd animationend",
function(e) {
if(
e.originalEvent.animationName == cssClass &&
e.target === e.currentTarget
) {
callback();
} else {
setAnimationListener();
}
}
);
}
self.addClass(cssClass);
setAnimationListener();
}
I used it kinda like this
cssAnimate.call($("#something"), "fadeIn", function() {
console.log("Animation is complete");
// Remove animation class name?
});
Original idea from http://mikefowler.me/2013/11/18/page-transitions-in-backbone/
And this seems handy: http://api.jqueryui.com/addClass/
Update
After struggling with the above code and other options, I would suggest being very cautious with any listening for CSS animation ends. With multiple animations going on, this can get messy very fast for event listening. I would strongly suggest an animation library like GSAP for every animation, even the small ones.
The accepted answer currently fires twice for animations in Chrome. Presumably this is because it recognizes webkitAnimationEnd as well as animationEnd. The following will definitely only fires once:
/* From Modernizr */
function whichTransitionEvent(){
var el = document.createElement('fakeelement');
var transitions = {
'animation':'animationend',
'OAnimation':'oAnimationEnd',
'MSAnimation':'MSAnimationEnd',
'WebkitAnimation':'webkitAnimationEnd'
};
for(var t in transitions){
if( transitions.hasOwnProperty(t) && el.style[t] !== undefined ){
return transitions[t];
}
}
}
$("#elementToListenTo")
.on(whichTransitionEvent(),
function(e){
console.log('Transition complete! This is the callback!');
$(this).off(e);
});
Chainable one-way events with promises
In case that you need one-way events just like JQuery's one(), I found this pattern handy:
function awaitTransitionEnd(transitionProperty, el, triggerFunction) {
return new Promise((resolve, reject) => {
const handler = (e) => {
if (e.propertyName !== transitionProperty) {
return;
}
el.removeEventListener('transitionend', handler);
resolve(e);
}
el.addEventListener('transitionend', handler);
triggerFunction(el);
});
}
You can then chain CSS transitions like in this example:
awaitTransitionEnd(
'background-color', myEl, () => myEl.classList.replace('bg-red', 'bg-green')
).then(() => awaitTransitionEnd(
'opacity', myEl, () => myEl.classList.add('opacity-0')
)).then(() => awaitTransitionEnd(
'opacity', myEl, () => myEl.classList.remove('opacity-0')
));
If you don't want to use arrow functions, you must pass the event + element like so:
awaitTransitionEnd('background-color', myEl, function(el) {
el.classList.replace('bg-red', 'bg-green');
}).then(function(e) {
return awaitTransitionEnd('opacity', e.target, function(el) {
el.classList.add('opacity-0');
});
}).then(function(e) {
return awaitTransitionEnd('opacity', e.target, function(el) {
el.classList.remove('opacity-0');
});
});
When awaitTransitionEnd is a class method and you don't want to use arrow functions, you must bind this to each then()-closure:
//[...]
.then(function(e) {
return this.awaitTransitionEnd('opacity', e.target, function(el) {
el.classList.add('opacity-0');
});
}.bind(this)).then(//[...]