How to event handle ALL focus() and blur() triggers on a page? - javascript

I have a Timer in javascript that's doing stuff in the background every xxx seconds. however the ajax work in the background seems to interrupt the users typing. I fix this by disabling the timers when focus/blur like one would, however, how can i make this site wide?
I want a way to magically detect any focus/blur events happening in any inputbox/textarea element and call some code, for both blur and focus.. is there a way to achieve this that will work without individually settings up the handlers for each of them?

You may try something like this:
$('input, textarea').on({
focus: function(e) {
// ...
},
blur: function(e) {
// ...
}
});
If you want same function for both events then (probably you don't want this):
$('input, textarea').on('focus blur', function(e) {
// ...
});

Related

jQuery disable clicks temporarily

My website has lots of elements which trigger ajax data retrieval on click, and some drag&drop elements handled by jquery ui. Many elements use their own click events, attached directly to them. I need some functionality which will ignore all mouse clicks/mouseup/mousedown events temporarily under some predefined circumstances. For example, I want to disable drag & drop entirely until some ajax request is in progress, etc. I thought I would bind on click and preventDefault(), I tried to bind on document, like this, but it doesn't seem to work:
$(document).on("mousedown", "*", null, function(ev){ev.preventDefault();});
I think it's because when the event reaches $(document), it was already triggered on all childs, so it's too late to preventDefault().
One solution I could imagine is to set some global variable, like ignore_clicks=true; and then add to every function which handles mouse click a check if this variable is true or not. This seems very difficult and I'm afraid even impossible due to external click handlers like in jquery-ui code.
Another solution I imagine is to temporarily put some fixed style element, 100% width and 100% height, zero opacity, over the current page, but this doesn't seem like an ideal solution, feels more like a hack. Furthermore if there is any ongoing animation on the webpage while it is covered by transparent element, the performance of the animation is degraded.
Is there any simple and elegant solution which would allow me to temporarily block all mouse clicks on the given page? (mouseup & mousedown too).
One solution is to stop the event in the capturing phase by using addEventListener:
document.addEventListener("mousedown", function(e) {
e.preventDefault();
e.stopPropagation();
}, true /* true == capturing phase */);
Do note that this won't work in IE8.
With jquery you can toggle on/off event handlers:
function doClick(e) {
e.preventDefault(); //Prevent default event.
//do some fancy ajax stuf...
};
//Toggle on:
$(document).on('click', '.clickable', doClick);
//Toggle off:
$(document).off('click', '.clickable', doClick);
This will work with any event such as click/mousedown/mouseup etc.
If you want to prevent all other events from being fired, you could try as follows:
function preventPropagation(e) {
e.stopImmediatePropagation();
};
//Toggle on when ajax:
$(document).on('click mousedown mouseup', '*', preventPropagation);
//Toggle off when ajax finishes:
$(document).off('click mousedown mouseup', '*', preventPropagation);
If you want to prevent all mouse-based interactions, you could place an invisible overlay in front of the dom, e.g.
HTML
<div id="click-blocker" style="display: none;"></div>
CSS
#click-blocker {
position: fixed;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
JS
// to disable clicks:
$('#click-blocker').show()
// to enable clicks again
$('#click-blocker').hide()
By showing the click-blocker, all clicks will happen to the blocker, since it's in front of everything, but since it has no content or background, the user will perceive it as their clicks are just not doing anything.
This won't work if you only want to disable a specific type of interaction, e.g. drag+drop as you mentioned.
I do not think having a "processing" animation overlay is a hack and it seems pretty elegant to me. Plenty of applications use this because it gives users feedback. They know that the application is working and they'll have to wait.

Run anonymous function on current & future elements?

I want to be able to run an anonymous function on specified future elements once they become part of the DOM. I couldn't find an answer to this. I need a cross-browser event that bubbles, and only runs once when the element is created, or ready, or something along those lines.
Internet Explorer has the "activate" event which is just what I need, except it is only in IE. I tried using the DOMActivate event in Chrome, but it behaves strangely when I tested it on a text input box. It would only fire when the element is clicked on, and it fired twice in a row. It acted like a focus event that is triggered twice, which is not helpful.
Here is an example of what I am trying to accomplish:
$('body').on('activate', '.date-picker', function () {
$(this).datepicker({
dateFormat: 'mm/dd/yy'
});
});
$('body').on('DOMActivate', '.date-picker', function () {
$(this).datepicker({
dateFormat: 'mm/dd/yy'
});
});
I realize I can accomplish this with future elements using a callback after I place them on the DOM, or by triggering my own event, but I was looking for a cleaner solution.
EDIT:
I got it to work like this:
$('body').on('activate', '.date-picker', function (e) {
$(this).datepicker({
dateFormat: 'mm/dd/yy'
});
});
var activateSupported = 'onactivate' in document.documentElement;
if (!activateSupported) {
$(document).bind('DOMNodeInserted', function (e) {
var target = $(e.target);
target.trigger('activate');
target.find('*').trigger('activate');
});
}
This isn't really ideal as it has to make all those extra function calls in browsers other than IE. Anyone have better solutions?
Due to how this plugin works (it is not visible until you focus the input,) you can lazy-bind the plugin using event delegation.
$(document).on("focus", ".date-picker", function (e) {
e.preventDefault();
$(this).removeClass(".date-picker").datepicker({dateFormat: 'mm/dd/yy'}).focus();
});
The first time it is focused, it is initialized. After it is initialized, the focus event is re-triggered to cause the datepicker to open. By removing the .date-picker class, I prevent it from re-initializing the datepicker on subsequent focus events.

Function running multiple times jQuery

Below is a function attached to the keyup event, this function works as it should on the first keyup, but on each subsequent keyup it runs the function multiple times. So for example, on first keyup event the function runs 1 time, on the 2nd keyup event the function runs 2 times, on the 3rd keyup it runs it 3 times and so on.
$(document).on('pageinit','#address_page,#edit_address_page', function(){
$(".names").focus(function() {
$(this).on('keyup', function() {
// Do Something
});
I have read other posts on this issue, but was unable to apply it to my scenario. Can someone please advise on this.
You're re-binding the keyup event every time the element is focused.
If you want to bind like that, you should also have a blur event to unbind it. Really, though, you can probably safely just bind the keyup and not worry about the focus. That element will only actually trigger a keyup WHEN it is focused.
$(".names").keyup(function () {
// do stuff
});
The problem is every time the input is focused a new keyup handler is added so try(not sure about the jquery mobile syntax and need for pageinit)
$(document).on('pageinit', '#address_page, #edit_address_page', function () {
$(this).find('.names').on('keyup', function () {
// Do Something
});
});

Is it possible to assign prevent default for all the events of an element at once? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
jQuery - How can I bind all events on a DOM element?
Imagine, if we want to make some element completely initeractable.
We could, of course bind a prevent default for a click event as follows:
$('form *').bind('click', function(event) {
event.preventDefault();
});
But that's just one event, and there are many more, like hover, focus, selectstart, and many more.
We could specify them all in one line like 'click focus hover dblclick blur selectstart' but that doesn't make much sense and is not easy to maintain.
So, is it possible to bind an event listener without discriminating for the type of the event? Maybe some native JavaScript listeners allow it?
No such possibility because not all elements support same events and not all events behave in the same way. You always have to explicitly provide a list of events whether defined statically or dynamically by a script that spits out event names.
Even though I linked to a script that creates an array of event names, these are made on one element only. You should of course be generating this with a more complex and slower script that enumerates over all elements in question and adds missing events. Using Javascript objects as associative array for faster searching whether a particular event has been added or not.
A better suggestion
What you're trying to do is likely a highly over-engineered solution. When I'm creating a demo clickable interface that should disable some elements (be it links, buttons or anything else) I rather do it by defining a CSS class that disables an element in question and have a simple script that does disabling afterwards.
You could leverage this even further by also providing which events you'd like to disable on particular element (with default being a click event).
<!-- no events; use defaults -->
No-follow link
<button class="disable">Nothing happens</button>
<!-- provide events -->
No-follow link
<form class="disable" data-events="submit">...</form>
Script
$(function() {
var disable = function(evt) {
evt.preventDefault();
console.log("Prevented on " + evt.target.tagName);
};
$(".disable").each(function() {
var ctx = $(this);
ctx.bind(ctx.data("events") || "click", disable);
});
});
Using smart defaults
Upper example defines one single event default. click event. This is fine and works in majority of cases, but not in all. form elements for instance would always have to define submit event that should be disabled. So. Smart defaults then. We should also consider the fact that list events that need supression is usually short. And if we cover majority of cases using defaults we only have a small overhead on those elements that actually do deviate from defaults.
$(function() {
// click is still default event
// this object defines per element events that aren't just click
var extraDefaults = {
form: "submit"
};
var disable = function(evt) {
evt.preventDefault();
console.log("Prevented on " + evt.target.tagName);
};
$(".disable").each(function() {
var ctx = $(this);
ctx.bind(
// use inline-defined events
ctx.data("events") ||
// use extra defaults if present
extraDefaults[this.tagName.toLower()] ||
// just use default click event
"click",
disable);
});
});
You can bind most jQuery events like this :
$("#elementID").on(Object.keys(jQuery.event.fixHooks).join(" "), function(e) {
e.preventDefault();
});
This will preventDefault on the following events :
click dblclick mousedown mouseup mousemove mouseover mouseout
mouseenter mouseleave keydown keypress keyup contextmenu
FIDDLE
Well after considering all the options, it still does not look convenient for all this event hustling. As it also has to bind the handlers for each event individually the script will hit the performance as well.
I am going to stick with a much simpler solution, just putting a div with transparent bg on top to cover our element.
$('form').css('position','relative').prepend($('<div class="mask" style="position:absolute;z-index:9000;height:100%;width:100%;background-image:url(1px_transparent.png);"></div>'));
Which is going to automatically fill the whole area of the element, alternatively, we can use a half-transparent picture so it will be also understood by a user that this is locked element, and would not cause confusion.
And to unlock we simply remove the .mask div from our element.
EDIT
New Fiddle: http://jsfiddle.net/YAdXk/8/
Actually we can disable tabbing by setting tabindex attribute to -1
.find('input,textarea,select').attr('tabindex','-1');
The updated fiddle prevents from tabbing as well.
EDIT2
OR, we can extend jQuery to use our custom lock() and unlock() functions on any element.
See the last fiddle: http://jsfiddle.net/YAdXk/13/
(function($) {
$.fn.lock= function() {
return this.each(function() {
$(this).css('position','relative').prepend($('<div class="mask" style="position:absolute;z-index:9000;height:100%;width:100%;background-image:url('+transparent_picture+');"></div>')).find('input,textarea,select').attr('tabindex','-1');
});
};
$.fn.unlock= function() {
return this.each(function() {
$(this).find('*').removeAttr('tabindex').filter('.mask').remove();
});
};
})( jQuery )
var all_events = "click blur focus mouse"; //etc...
$('form *').bind(all_events, function(event) {
event.preventDefault();
});
Now is easier to maintain ;)
jQuery defines all shortcut event types here, so you can use that string to store all events for re-use:
var events = "blur focus focusin focusout load resize scroll unload click dblclick " +
"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
"change select submit keydown keypress keyup error contextmenu";
$('button').bind(events, function() {
// hey
});
Yes, it is possible, to catch all events of one type at once! But you'll need to specify all the event types explicitly.
Your code example of "form *" is inefficient, and would not catch events on elements that are added after your code executes.
Because of the bubbling effect of javascript events, you can assign a catch all event handler on the most parent element, eigther $("form") or $("body"), and add preventDefault() to that.
Example code:
$("a").on("click", function() {
$("body").append("<p>Clicked...</p>");
});
$("body").on("click", function(e) {
e.preventDefault();
});
with:
<div>
<p>Click on me</p>
</div>​
On JSfiddle: http://jsfiddle.net/erlang/EHeBK/
The concept of catching all events on a parent element, is often referred to as event delegation.

JS jQuery Hiding element on unfocus stops other events from being fired

I have a search suggestion box that I hide when the search text box loses focus. This works great, except that when I click one of the suggestions the click event for that suggestion does not fire.
searchText.focusout(function () { $("#search-suggestions").hide(); });
I also tried:
searchText.focusout(function () { $("#search-suggestions").css("visibility", "hidden"); });
I tried commenting out the hide on unfocus code and the click events then worked fine.
(Basically, the blur event happens before the click on the suggestion can be registered, such that the element I attempted to click is not on the screen when the clicm does register)
here's the click event code:
//Called after the ajax load
$("#search-suggestions").find("a").click(function () { alert("hi"); })
I also tried rendering this on the server but it failed as well:
Search Suggestion
If any one has any suggestions I would appreciate it. Thanks!
You could try to define something like this:
//this goes where you first binding focusout handler
searchText.focusout(onFocusOut);
//this is a usual function
function onFocusOut() {
$("#search-suggestions").hide();
}
//this could be defined after you draw the search-suggestions control
$("#search-suggestions").hover(function() {
//this is hover in handler; unbind focusout from searchText
//something like that:
$("#searchText").unbind('focusout', onFocusOut)
}, function() {
//this is hover out handler; bind focusout to searchText
//something like that:
$("#searchText").bind('focusout', onFocusOut)
});
you could also use live (http://api.jquery.com/live/) to define hover handler for #search-suggestions, depending on what exactly you need.
This will make your search suggestions stay visible when clicking them. In click handler you can then hide them.
Try just making it invisible.
Change $('#my_search_box').hide(); to $('#my_search_box').css('visibility','hidden');
If you have surrounding DOM elements that need to act as if the search box is gone, you can just assign it an absolute position as well.
Try using .css('visibility', 'hidden') instead of .hide which uses display:none.

Categories