Disable then Re-Enable onClick() - javascript

I have a website that I am creating and there are different divs each with their own content. All of them are hidden except the main one that shows up on the homepage. The transitions are pretty long, and I like it that way, but the problem is that if somebody spams the navbar buttons it opens up all those divs ontop of each other. So to prevent that I want to temporarily disable the onClick for an <a></a> element. Then enable it after the transition is done. Currently I am able to disable it, but cannot find a way to re-enable it.
function disable(){
$(".bar").unbind();
$(".bar").removeAttr("onClick");
}
I know how to call a function after a certain amount of time, but what is the "enable" equivalent to the code in this function?

The exact opposite would be to set the onClick back on the element.
$('.bar').attr('onClick', function() { alert('blah'); });
or with vanilla js
document.querySelector(".bar")
.setAttribute('onClick', function() {...});
However, this is difficult to manage for many elements with the same functionality. It would be easier to have this entirely managed with javascript (and jQuery in this case).
function clickEvent(event) {
var self = $(this);
// Unbind the event
self.off(clickEvent);
// Click logic here
// Rebind event
self.on('click', clickEvent);
}
$('.bar').on('click', clickEvent);

Instead of disabling the event on the DOM, you can just add an extra piece of logic to your dynamic divs:
$('#mydiv').click(function() {
if(!inTransition())
// DO A THING
else
// DON'T DO A THING
});
As a side note: If you're doing a lot of dynamic DOM manipulation, you may want to look into using a data binding framework such as Angular or Knockout. jQuery is nice for simple DOM manipulations, but it can quickly become messy and hard to maintain if you're doing something complex (which it sounds like you are).

As somewhat of an extension to nmg49's answer, I'd like to provide a solution that's a little more in depth.
Essentially what you'll want to do is create a flag to determine whether or not you are currently transitioning, and cancel the onClick if it is true (disabling it after the transition is complete).
var isTransitioning = false;
$('.bar').onClick(function(){
if(isTransitioning) return;
isTransitioning = true;
// DO TRANSITION
});
Once the transition is complete, you simply set isTransitioning to false (either in a callback, or at the end of your onClick function; which ever one applies to your code).
This will ensure that, no matter how many times they click the button, they will not be able to transition if they're already in transition.

Related

Prevent anchor tag to jump

It is my first post on SO, I am using it already quite a while, always found a solution via search. Now I'm starting to dig deeper into programming - right now learning Java Script - and I couldn't find the exact answer for my beginner problem:
I've created a simple photo gallery where the thumbnails point to the image via href. The images are not displayed by default. By clicking on a thumbnail, the corresponding image appears thanks to the :target pseudo element. This way I can bypass the cascading nature of my HTML structure and address elements higher in hierarchy.
See fiddle:
https://jsfiddle.net/ahu1kaqf/
The problem is, that this "hack" has the side effect of putting the image to the very top of the window due to its default anchor jump behavior.
So what I want to accomplish is to turn off or bypass just the jump behavior.
Therefore, solutions with JS like "preventDefault" or "return false" are not suitable as they turn off the complete anchor behavior.
My idea was to read the yScroll position just before the click and pass it to another function which triggers just after the page jump. By appending an onclick event on the anchor tag I found out that the function executes before the actual jump and I can read the current scrollY position:
function posY(){
console.log(window.scrollY);
scry = window.scrollY;
}
Then, after the anchor event has finished, I would like to pass the variable scry to another function which triggers just after the anchor jump to undo the jump:
function undoJump(){
window.scrollTo(0, scry);
}
It doesn't really work with a click event as the function triggers before the actual jump.
The js code in the fiddle is in script tags because putting just the functions into the js window (of course without script tags) shows an error in the console, I don't know why...
Sorry, I'm really a beginner, thank you all for your help!
https://jsfiddle.net/ahu1kaqf/1/
var classname = document.getElementsByClassName("thumb");
for (var i = 0; i < classname.length; i++) {
classname[i].addEventListener("click", posY);
}
function posY(e){
e.preventDefault();
console.log(window.scrollY);
}
Remove the onclick as it's just not the best way to do it. Add a class to the links OR use querySelectorAll based on the parent class. The benefit here is, you don't have to remember to add a class or a onclick. It's less code to deal with and easier to manage.
https://jsfiddle.net/ahu1kaqf/2/
In either case, you can stop the normal event behavior with e.preventDefault();
For the second part, you'll probably just want to set a global in this case. Set a global outside all of the functions and change it on click. You could probably get more complex with objects, and promises and such, but, honestly, it'd be just as easy to set a global value. It's good to try to avoid them, but they can be simple useful solutions depending on the overall complexity of the application in question.
Yes, you need to prevent the default action (which is a navigation action).
In modern browsers, that means you can do this (note I'm passing the global event object as a parameter):
<div class="thumb">thumb1</div>
Then in your js code you can do this:
function posY(e){
e.preventDefault()
console.log(window.scrollY);
}

jQuery toggle on click out

I have a series of spans (togglers) and a series of divs (toggled). I created a make_toggle function that receives the toggler and its corresponding toggled as arguments.
Everything seems to work kind of ok up to the point where I try to implement a "toggle on click out". What I've tried is to attach to the html click event a function that checks whether the target of the click is contained within the toggled element. On toggle "back", I would then detach the handler so I am only checking when I need.
var check_if_clickingout = function(e) {
if (!toggled[0].contains(e.target)) {
toggle();
}
};
See fiddle: https://jsfiddle.net/andinse/65o211nc/11/
It doesn't even seem to work anymore but when it used to, it was triggering many more times than necessary (which was the reason for me to come here ask for help).
What am I doing wrong? What is the most effective way to go about this kind of situation where I am giving functionality to a series of independent DOM elements?
Just putting this out here that this seems to do the same thing.
$("span").click(function() {
$(this).siblings("div").toggleClass("blue");
});
Maybe I am missing something more that I am not seeing in your example.
See more: http://api.jquery.com/toggleclass/

YUI: Stop event handlers from triggering while an animation is running

So I'm using YUI to add some animations in my application triggered when a user clicks certain elements. However, I'm running into a common problem that is easily fixed with some poor coding, but I'm looking for a more elegant solution that's less error-prone.
Often, when the user clicks something, a DOM element is animated (using Y.Anim) and I subscribe to that animation's 'end' event to remove the element from the document after its animation has completed. Pretty standard stuff.
However, problems arise when the user decides to spam-click the element that triggers this event. If the element is going to be removed from the DOM when the animation ends, and the user triggers an event handler that fires off ANOTHER animation on the same element, this 2nd animation will eventually cause YUI to spit really nasty errors because the node it was animating on suddenly disappeared from the document. The quickest solution I've found for this is to just set some module/class-level boolean state like 'this.postAnimating' or something, and inside the event handler that triggers the animation, check if this is set to true, and if so, don't do anything. In the 'end' handler for the animation, set this state to false.
This solution is really, really not ideal for many reasons. Another possible solution is to detach the event handler for duration of the animation and re-attach it once the animation is complete. This is definitely a little better, but I still don't like having to do extra bookkeeping that I could easily forget to do if forgetting to do so leads to incomprehensible YUI errors.
What's an elegant and robust way to solve this problem without mucking up a multi-thousand-line Javascript file with bits and pieces of state?
Here's some example code describing the issue and my solution to it.
var popupShowing = false,
someElement = Y.one('...');
someElement.on("click", showPopUp)
var showPopup = function(e) {
if(!popupShowing) {
popupShowing = true;
var a = new Y.Anim({
node: someElement,
duration: 0.2,
...
});
a.on('end', function() {
someElement.remove(true);
popupShowing = false;
});
a.run();
}
}
So if the user clicks "someElement" many times, only one animation will fire. If I didn't use popupShowing as a guard, many animations on the same node would be fired if the user clicked quickly enough, but the subsequent ones would error out because someElement was removed when the first completed.
Have a look at the Transition API. It's more concise, and may very well do what you want out of the box.
someElement.transition({ opacity: 0, duration: 0.2 }, function () { this.remove(); });
// OR
someElement.on('click', function () { this.hide(true, { duration: 0.2 }); });
// OR
someElement.on('click', someElement.hide);
Personally, I haven't used Anim since Transition was added in 3.2.0. Transition uses CSS3 where supported (with hardware acceleration), and falls back to a JS timer for older browsers.
http://developer.yahoo.com/yui/3/examples/transition/transition-view.html
Edit: By popular demand, a YUI way:
myAnim.get('running')
tells you whether an animation is running. To use this you might have to restructure the way you call the event so the animation is in the right scope, for example:
YUI().use('anim', function (Y) {
var someElement = Y.one('...');
var a = new Y.Anim({
node: someElement,
duration: 0.2,
...
});
a.on('end', function() {
someElement.remove(true);
});
someElement.on('click', function() {
if (!a.get('running')) {
a.run();
}
});
});
jsFiddle Example
Previously I had said: I personally like the way jQuery handles this. In jQuery, each element has a queue for animation functions. During animations an "in progress" sentinel is pushed to the front for the duration of the animation so anything that doesn't want to step on an animation peeks at the front of the queue for "in progress" and decides what to do from there, e.g. do nothing, get in line, preempt the current animation, etc.
I don't know enough about YUI to tell you how to implement this, but I find it to be a very elegant solution.
Quick & dirty solution to this particular issue?
Attach your handlers using .once() instead
someElement.once("click", showPopUp)
Also suitable if you need the handler re-attached later, just call that line again when the animation is done. You could also store your state information on the node itself using setData/getData but that is just a panacea to the real problem of state tracking.
Also, +1 to Luke's suggestion to use Transition for DOM property animation, it's grand.

Javascript mootools delay hide/show multilevel menu

I've made a javascript menu with css and javascript. I've used some mootools (1.11 , the only version i can use).
The script runs on domready, it fetches a dom element, and adds functions (showmenu, hidemenu) on the mouseenter and mouseleave events. The dom element is three levels of nested ul/li`s.
Now I want to add a delay on the menu of 500 ms when one hovers over the menu for the first time, and again when the users leaves the menu (so that the user has half a second time to get back to the menu).
I dont know how to keep track of the events, and cancel them. My knowledge of javascript is not good enough to know where to start. Can anyone give me an example how i should create this? Not really looking for cut and paste code, more pointers in the working of javascript, which native functions i could use, and what is the best way to set something like this up.
Thanx in advance
p.s. Maybe i want to also have a delay (100 ms or so) when the user is already using the menu for the items to show up. Will this be a lot more complex?
perhaps this can give you an idea: http://www.jsfiddle.net/dimitar/stthk/ (extracted it from another menu class I am working on and modded for delay for you as an example)
basically several interesting bits:
options: {
showDelay: 500,
hideDelay: 500
},
defines your delays on mouseover and out.
and then the bind for mouseenter deferred via .delay():
mouseenter: function() {
$clear(_this.timer);
_this.timer = (function() {
this.retrieve("fold").setStyle("display", "block");
}).delay(_this.options.showDelay, this);
},
mouseleave: function() {
$clear(_this.timer);
_this.timer = (function() {
this.retrieve("fold").setStyle("display", "none");
}).delay(_this.options.hideDelay, this);
}
_this.timer is a shared var that handles the deferred function - it gets cleared up on either mouseout or mouseover. if no event that matters takes place within the allotted time period, it will change the display accordingly, else, it will cancel the function.
this is for mootools 1.2.5 btw (storage system + elment delegation) but the principle remains the same for the bits that matter.
The stylish/anal way of doing it would be to fade in/out the menu. You do that with Fx.Morph where you morph the opacity css property and set the complete property to actually remove the div - notice that it's different to make this work in IE5-7.
The more basic/sensible way is to use setTimeout().

Need to cancel click/mouseup events when double-click event detected

How is this done?
This is a good question, and I actually don't think it can be done easily. (Some discussion on this)
If it is super duper important for you to have this functionality, you could hack it like so:
function singleClick(e) {
// do something, "this" will be the DOM element
}
function doubleClick(e) {
// do something, "this" will be the DOM element
}
$(selector).click(function(e) {
var that = this;
setTimeout(function() {
var dblclick = parseInt($(that).data('double'), 10);
if (dblclick > 0) {
$(that).data('double', dblclick-1);
} else {
singleClick.call(that, e);
}
}, 300);
}).dblclick(function(e) {
$(this).data('double', 2);
doubleClick.call(this, e);
});
And here is an example of it at work.
As pointed out in the comments, there is a plugin for this that does what I did above pretty much, but packages it up for you so you don't have to see the ugly: FixClick.
Raymond Chen has discussed some of the implications of single-versus-double clicking - although he's talking in the context of Windows, what he says is relevant to browser-based UI design.
Basically, the action taken on a double click should be a logical thing to do after a single click. So for example, in a desktop UI, single click selects an item, and double click opens it (e.g. opens the file, or launches the application). The user would have to select the file to open it anyway, so it doesn't matter that the single click action is taken before the double click action.
If you have a UI component whose double click action is completely unrelated to the single click action, such that it becomes necessary to prevent the single click action from occurring once the system realises it was actually a double click, then you really ought to rethink your design. Users will find it awkward and counter-intuitive, in that it will not act in the way they are used to things acting.
If you still want to go that way, then you will either have to use the debouncing technique (in which case all single click actions will be delayed) or else implement some mechanism whereby the double click handler undoes the work done by the single click handler.
You should also be aware that some users set a very long double click time. Somebody with, for example, arthritic hands might have a double click time of more than a second set in their system preferences, so the debouncing technique based on some arbitrary time period of your choosing is going to make your UI component inaccessible to those people if taking the single click action precludes taking the double click action. The "undo what just happened on single click" technique is the only viable workaround for this, as far as I know.
The technique outlined in the other answers is known as debouncing.
jQuery Sparkle provides a clean elegant solution for this, by implementing a singleclick custom event. By doing this, you can use it just like any other event, so:
$('#el').singleclick(function(){});
// or event
$('#el').bind('singleclick', function(){});
It also provides custom events for the last and first clicks of a series of clicks. And the lastclick custom event actually passes the amount of clicks back! So you could do this!
$('#el').lastclick(function(event,clicks){
if ( clicks === 3 ) alert('Tripple Click!');
});
You can find the source code for defining the custom event here.
It's open source under the AGPL licence, so you can feel free to grab what you need out of it worry free! :-) It's also actively developed on a day to day basis so you will never be short on support.
But most importantly it is a DRY Plugin/Effect Framework to allow you to develop plugins and extensions much more easily. So hope this helps to achieve that goal!
If this is for a button submitting a form (which is not necessarily the case for the original poster, but may be the case for other people getting here via Google), an easier option would be to disable the element that is being clicked on in your click event handler:
$(selector).click(function(e) {
$(this).prop('disable', true);
}

Categories