I'd say you're on the right track with all your code. The trick is probably to abstract some of the oft-repeated code into functions/plugins.
For the "singleton selector", wrapping your code in a plugin would seem the way to go. But note that your current code might need tweaking. You have this line:
$(".selected").removeClass("selected")
which will remove the selected
class from elements anywhere on the page. In your example, you'd probably want only to remove the class from .entity
elements within the #tempo
element.
Here's a (very quick) sketch of a plugin, that should do what you want (and keep it limited to the container element):
$.fn.singleSelection = function(classSelector, handler) {
return this.each(function() {
var container = $(this);
container.on("click", classSelector, function (event) {
container.find(classSelector + ".selected").removeClass("selected");
$(this).addClass("selected");
if( typeof handler === "function" ) {
handler.call(this, event);
}
});
});
};
Your code can then be shortened to
$("#tempo").singleSelection(".entity"); // you can add a click handler too
Here's a demo.
But... jQueryUI already has this functionality, so if you feel like using that, go right ahead. It's certainly been tested better than what you see above :)
However, for such a simple thing, jQueryUI seems like overkill. But there are probably other, leaner plugins you can find, which'll do what you need - this is just an example.
For your second point, I'd probably wrap the code in an addFolder
function somewhere. But it's a little hard to be more specific without knowing more about the context.
For your third point, you could again make a simple jQuery plugin that'd only forward events to your click handler if the element doesn't have the "clicked" class. There are also various "debounce" plugins out there to throttle clicks, but most simply impose a time limit rather than explicitly wait for an ajax operation to finish.
In any event, here's another (very quick) sketch of a jQuery plugin
$.fn.clickOnce = function(handler) {
return this.each(function() {
var element = $(this);
element.on("click", function (event) {
if( !element.hasClass("clicked") ) {
element.addClass("clicked");
if( typeof handler === "function" ) {
handler.call(this, event);
}
}
});
});
};
In your code you could then do:
$("button#new_game").clickOnce(function (event) {
// this will only be called if the button wasn't already clicked
var button = $(this);
createAjaxCall(function (result) {
// it's your responsibility to remove the "clicked" class
button.removeClass("clicked");
});
});
Here's a demo
Again, there are no doubt ready-made plugins that do this and do it better.
By the way, I personally find it easier to pass jQuery's XHR objects around, as they act as deferred objects/promises. I.e.:
var xhr = $.ajax(...); // create an XHR obj from any of jQuery's ajax functions
xhr.done(function (data) {
// success handler
});
xhr.fail(function (err) {
// failure handler
});
xhr.always(function () {
// complete handler
});
So if your createAjaxCall()
function returns an xhr object, you can attach a handler to the always
"event", and use that to clear the clicked
class. That seems more robust (and readable) to me.
p.s. You could use a data-*
attribute (set with .attr()
instead of .data()
), and still have custom styling with CSS like
button[data-clicked='true'] { // CSS attribute selectors are neat!
...
}
but browser support is limited compared to using simple classes.