I've encountered a weird issue - accessing any DOM element's outerHeight, or simply logging that element to browser's console in a blur event seems to cancel the click event that caused the blur - when some special criteria is met.
Here's what I'm trying to do:
There are 2 inputs, first is initially visible, the other hidden
When the first input receives focus, the 2nd input will be shown
When the 2nd input receives focus, it remains shown
When both inputs lose focus, and none of them are focused, the 2nd input will be hidden
However, it seems that when I simply try to do something like $('body')[0].outerHeight in my blur event handler, the second input never receives the click or focus. Since it's too much to simply write down, here's a link to JSFiddle that demonstrates the issue: http://jsfiddle.net/7K2Ha/3/
Note - it happens the same with plain JS
Can anybody explain why this happens and are there ways around this?
Firstly, you bind focus/blur events to all .jq inputs. And then you add next event handlers (which means, that all of them will be executed - including the first one, which removes the has-focus class).
Check this fiddle, line 20: fiddle here.
$('#input3').unbind('blur').on('blur', //...
The problem is not specific to offsetHeight, and I believe if you comment out that line you don't get the effect simply because the browser will not call an empty function block for performance/optimizer reasons.
It seems to come down to the execution order of the events (especially problematic when more than one jquery event of the same type is wired up).
Check out what happens when you wire up the blur/mouseover/etc. events that all $('input.jq') elements have in common at the very end: http://jsfiddle.net/7K2Ha/6/
All I did was move the topmost javascript block to the bottom, and suddenly it works. In that case, the focus event seems to occur before the blur event. Notice how before the focus would never be called.
As a more robust solution, I'd only work with one event that covers both:
$('input.jq')
.on('focus', function() {
// get focus'ed elements parent
var thisParent = $(this).parent('div')
thisParent.addClass('has-focus');
// get all other parents
var otherParents = $('input.jq').parent('div').not(thisParent);
otherParents.removeClass('has-focus');
})
See updated JSfiddle.
Related
In my focus manager I need to store some info on the last focus and blur. I need to delegate this hook to body, but if I use * as a filter I will receive the focus event for every parent item of the actual thing getting focus.
Realistically, I could make a filter to accept something like a, input, button ... etc, but also I need to refine it to a:not([tabindex]=-1), *[tabindex!=-1] etc.
But this gets complicated when one considers disabled controls. Is there a jQuery selector for :focusable, or how do I work around this? I could debounce my function, but even that is iffy.
EDIT:
Alright, my bad, I think what I am looking for here is :tabbable.
you can check focus by using: $("yourselector").is( ":focus" )
If you want to stop bubbling up to the dom just return false in your function after doing your stuff.enter code here
I have a <form> containing two text <input>s side-by-side. When both of these inputs lose focus, I want to do some stuff (send an Ajax request to server and replace them with something else in the DOM). However if I click or tab from one of the inputs to the other, I don't want to do those things.
Setting an onBlur event handler on the <form> element works fine using my setup, but there doesn't seem to be any way to determine if the other form input is my next target. If I examine document.activeElement in my handler, it points to the <body> element (in Chrome) at that point. Only afterwards does it change to the other input.
Is there any way to reliably do what I'm asking? Solutions involving jQuery or other libraries are fine.
So it turns out that event.relatedTarget was what I was looking for - this will return the DOM node receiving focus on a blur event, so it's just a matter of setting a conditional to see if it matches the other field.
However, while this works great in Chrome, relatedTarget currently isn't implemented properly in other browsers. Apparently there's a couple of workarounds:
In IE11, document.activeElement does actually get set to the receiving element at the time the event fires, so you can use that.
In Firefox, apparently event.explicitOriginalTarget can be used instead.
Try ..
Set a global variable that stores what you last blurred from. Call it x.
Set a blur event on all elements, and set x to the last element you blurred from.
Set a focus event on all elements, and if x is not one of your two elements, then run your script.
Give to them both ids, like this:
<input id="id1" />
<input id="id2" />
Then, with jQuery, you can call blur() nested with the two ids.
$('#id1').blur(function(){
$('#id2').blur(function(){
//do your thing
});
});
I've got a form where I'm trying to do the sort of thing you often see with tags: there's a textfield for the first tag, and, if you put something into it, a new and similar textfield appears to receive another tag. And so on. I've gotten the basics of this working by setting up a jQuery .blur() handler for the textfield: after the value is entered and the user leaves the field, the handler runs and inserts the new field into the form. The handler is pretty vanilla, something like:
$('input.the_field_class').blur(function () { ... });
where .the_field_class identifies the input field(s) that collect the values.
My problem is that, while the new textfield is happily added to the form after the user enters the first value, the blur handler doesn't fire when the user enters something into the newly-added field and then leaves it. The first field continues to work properly, but the second one never works. FWIW, I've watched for and avoided any id and name clashes between the initial and added fields. I had thought that jQuery would pick up the added textfield, which has the same class markings as the first one, and handle it like the original one, but maybe I'm wrong -- do I need to poke the page or some part of it with some sort of jQuery initialization thing? Thanks!
Without seeing your code in more of its context, it's hard to know for sure, but my best guess is that you're attaching a handler to the first field, but there is no code that gets called to attach it to the new field. If that's the case, you have a few options, two of which are:
1) In your blur() handler, include code to attach the blur handler to the newly created field.
2) Use jQuery's event delegation to attach a handler to the field container, and listen for blur events on any field in the container:
<div class="tag-container">
<input class="the_field_class" /> <!-- initial tag field -->
</div>
<script>
var $tagContainer = $('.tag-container');
var createNewField = function() {
$tagContainer.append($('<input class="the_field_class" />');
};
$tagContainer.on('blur', 'input.the_field_class', createNewField());
</script>
Which is better will depend on your use case, but I'd guess that the 2nd option will be better for you, since you're unlikely to be dealing with tons of blur events coming from the container.
I have a large form that contains several text input fields. Essentially, I need to handle the onchange event for all fields and the onblur events for some fields. When a change is made to a field and the field loses focus, both events fire (which is the correct behavior). The only issue is that I would like to handle the onblur
event before I handle the onchange event.
After some testing in ie and Firefox, it seems that the default behavior is to fire the onchange event before onblur. I have been using the following code as a test...
<html>
<body >
<input type="text" value="here is a text field" onchange="console.log('Change Event!')" onblur="console.log('Blur Event!')" >
</body>
</html>
Which brings me to my questions:
It seems that this behavior is consistent across browsers. Why does onchange fire first?
Since I cannot handle the onblur event for every input element, is there a way I can get onblur to fire before handling the onchange event?
The reason onchange fires first is that once the element loses focus (i.e. 'blurs') the change is usually complete (I say usually because a script can still change the element without user interaction).
For those elements that need onblur handled first, you can disable the onchange handler and fire the onchange (or even a custom event) from the onblur handler. This will ensure the correct order even though it is more work. To detect change, you can use a state variable for that field.
As a general remark though, the need for such synchronicity is a sign that the approach you are using to solve whatever problem you are solving might need more work even though sometimes it cannot be avoided. If you are sure this is the only way, try one of these methods!
EDIT: Just to elaborate on the last point, you would have to follow some assumptions about your event model. Are you assuming that each change event is followed by a blur and goes unprocessed otherwise, or would you like to process each change but those that are followed by a blurget further processing after whatever onblur does with them? In any case if you want to enforce the order the handlers would need access to a common resource (global variable, property, etc.). Are there other event types you might want to use? (input?). Finally, this link has some details for the change event for Mozilla browsers:
https://developer.mozilla.org/en-US/docs/Web/Reference/Events/change.
The third 'bullet' addresses the issue of event order.
This is a bit of hack, but it seems to do the trick on most browsers:
<input type="text" value="Text Input" onchange="setTimeout(function(){console.log('Change Event!')}, 0);" onblur="console.log('Blur Event!');" />
You can see a fiddle of it in action here: http://jsfiddle.net/XpPhE/
Here is a little background information on the setTimeout(function, 0) trick: http://javascript.info/tutorial/events-and-timing-depth
Hope that helps :)
I'd like to have the browser act as if the user had pressed the Tab key when they click on something. In the click handler I've tried the following approaches:
var event = document.createEvent('KeyboardEvent');
event.initKeyEvent("keypress", true, true, null, false, false, false, false, 9, 0);
this.input.focus()[0].dispatchEvent(event);
And jQuery:
this.input.focus().trigger({ type : 'keypress', which : 9 });
...which I took from here.
The first approach seems to be the best bet, but doesn't quite work. If I change the last two parameters to 98, 98, indeed, a 'b' is typed into the input box. But 9, 0 and 9, 9 (the former of which I took right from the MDC web site) both give me these errors in firebug under FF3:
Permission denied to get property XULElement.popupOpen
[Break on this error] this.input.focus()[0].dispatchEvent(event);
Permission denied to get property XULElement.overrideValue
[Break on this error] this.input.focus()[0].dispatchEvent(event);
Permission denied to get property XULElement.selectedIndex
[Break on this error] this.input.focus()[0].dispatchEvent(event);
Permission denied to set property XULElement.selectedIndex
[Break on this error] this.input.focus()[0].dispatchEvent(event);
I've heard such (with no clear definition of 'such') events are 'untrusted', which might explain these errors.
The second approach causes whatever value I put as event.which to be passed as event.which, but to no effect (even if I use 98 instead of 9, no 'b' is typed in the box.) If I try setting event.data in the object I'm passing, it ends up undefined when the event is triggered. What follows is the code I'm using to view that:
$('#hi').keypress(function(e) {
console.log(e);
});
Any other ideas?
The solution I ended up going with is to create a "focus stealer" div (with tabindex = -1--can have the focus but can't be tabbed to initially) on either side of the area in which I want to manually manage the focus. Then I put a bubbling-true event listener for focus and blur on the whole area. When any focus occurs on the area, the tabindex values are changed to -1, and when any blur occurs, they're changed to 0. This means that while focused in the area, you can tab or shift-tab out of it and correctly end up on other page elements or browser UI elements, but as soon as you focus out of there, the focus stealers become tabbable, and on focus they set up the manual area correctly and shunt the focus over to the element at their end, as if you had clicked on one end or the other of the manual area.
This is the solution I used on our webapp for two custom controls, a pop-up calendar and a pop-up unit / value weight selector (clicking the text box pops up a div with two selects)
function tab_focus(elem)
var fields = elem.form.getElements()
for(var i=0;i<fields.length;i++) {
if(fields[i].id == elem.id){
for(i=i+1;i<fields.length;i++){
if(fields[i].type != 'hidden'){
fields[i].focus()
return
}
}
break;
}
}
elem.form.focusFirstElement();
}
This is using the Prototype framework and expects an extended element(ie $('thing_id')) as its parameter.
It gets the form the element belongs to, and loops through the elements of the form until it finds itself.
It then looks for the first element after it that is not hidden, and passes it the focus.
If there are no elements after it in the form, it moves the focus back the to first element in the form. I could instead find the next form on the page through document.forms, but most of our pages use a single form.
Actually, I guess there is a way, even if it's a major PITA. I can make sure that every element, even if naturally a tab-stop, has an Xtabindex, somehow in the proper order even though I'll be dropping in other people's widgets and so using jQuery to add these after the fact, rather than being able to specify it right in the HTML or other initial building code. Then, my entire form will have a real tabindex. While it has the focus, it will absorb keypresses, and if they're tab or shift+tab, move the fake focus based on Xtabindex. If tab is pressed on the last (or shift+tab on the first) element in the form, it won't gobble the keystroke, thus allowing the browser to properly focus on other page or browser UI elements outside the form using the keyboard.
I can only guess what kinds of unintended side-effects this approach will introduce.
Actually, it's not even a solution, because I still can't fake a tab on the last element using it.
I created a simple jQuery plugin which does solve this problem. It uses the ':tabbable' selector of jQuery UI to find the next 'tabbable' element and selects it.
Example usage:
// Simulate tab key when element is clicked
$('.myElement').bind('click', function(event){
$.tabNext();
return false;
});
I think those errors are from autocomplete. You might be able to turn them off by setting, before you dispatch the event, the autocomplete attribute to 'off'
setAttribute('autocomplete','off')