Temporarily removing "onblur" handler in IE - javascript

I wonder if someone here can help me figure out this problem.
I need to temporarily disable the code in the 'onblur' attribute of an input field, and then re-enable it after. The reason I need to do this is b/c my workflow is as follows:
1) An AJAX event is triggered by the "onblur" attribute of an input field, which resulted from the user tabbing off of the field to go to the next one.
2) The page is updated and my Javascript code is called from the "onafterajax" hook.
3) My code does some serious re-arranging of the DOM, which results in loss of focus on the current input field. So, I must restore focus manually. I've attached a focus handler to all input fields, so I can keep track of which field had focus last, so I know where to put the focus.
4) I call .focus() on the correct input field, which for some reason causes 'onblur' to fire on that same input field. This happens in all browsers. This a) suggests that the field still had focus?! and b) Creates an infinite loop, since Step 1 executes again...
In order to avoid the infinite loop, I've done the following. Before performing any DOM rearrangements, I do this:
if ( window.currentFocusId ) {
this.savedFocusId = currentFocusId;
this.onblur_bak = $(this.savedFocusId).onblur;
$(this.savedFocusId).onblur = null;
LOG.warn(" --- Disabling blur on " + this.savedFocusId);
}
Then, after I've completed my DOM hackery, I do this:
setFocusAndRestoreBlur : function(req) {
if ( this.savedFocusId ) {
var focusElement = req.form.elements[this.savedFocusId];
LOG.warn(" ------ Focusing on " + focusElement.id);
if (focusElement) {
focusElement.focus();
if (focusElement.select) {
focusElement.select();
}
}
LOG.warn(" --- Enabling blur on " + this.savedFocusId);
$(this.savedFocusId).onblur = this.onblur_bak;
delete this.onblur_bak;
delete this.savedFocusId;
}
}
This works well in all browsers (FF, Chrome, Opera), except for IE6.
In IE6 it kind of works. First of all, in order to make it work in IE at all, I had to modify the call to "setFocusAndRestoreBlur" like so:
setTimeout(function() {
this.setFocusAndRestoreBlur(req);
}.bind(this), 0)
This has the effect of running the code after the current thread finishes. For some reason IE does not respect the fact that I have removed the onblur handler, if I try to set the focus in the same thread!
So, with this modification, the first time I enter some numbers into a few fields, everything is fine. I get log output that looks like this:
Type something into j_id390 and tab off:
warn[16:35:33,091]: blur fired from statementProgramsDataTable:0:j_id390
warn[16:35:33,092]: --- Disabling blur on statementProgramsDataTable:0:j_id397
warn[16:35:33,225]: ------ Focusing on statementProgramsDataTable:0:j_id397
warn[16:35:33,225]: --- Enabling blur on statementProgramsDataTable:0:j_id397
Type something into j_id397 and tab off:
warn[16:35:38,259]: blur fired from statementProgramsDataTable:0:j_id397
warn[16:35:38,260]: --- Disabling blur on statementProgramsDataTable:0:j_id446
warn[16:35:38,390]: ------ Focusing on statementProgramsDataTable:0:j_id446
warn[16:35:38,390]: --- Enabling blur on statementProgramsDataTable:0:j_id446
However, when I go back to the first input field, overwrite the value and tab off, I get this:
warn[17:18:15,454]: blur fired from statementProgramsDataTable:0:j_id390
warn[17:18:15,469]: --- Disabling blur on statementProgramsDataTable:0:j_id397
warn[17:18:16,870]: ------ Focusing on statementProgramsDataTable:0:j_id397
warn[17:18:16,874]: --- Enabling blur on statementProgramsDataTable:0:j_id397
warn[17:18:18,097]: blur fired from statementProgramsDataTable:0:j_id397
warn[17:18:18,112]: --- Disabling blur on statementProgramsDataTable:0:j_id397
warn[17:18:19,550]: ------ Focusing on statementProgramsDataTable:0:j_id397
warn[17:18:19,555]: --- Enabling blur on statementProgramsDataTable:0:j_id397
warn[17:18:24,492]: blur fired from statementProgramsDataTable:0:j_id397
warn[17:18:24,187]: --- Disabling blur on statementProgramsDataTable:0:j_id397
warn[17:18:24,531]: ------ Focusing on statementProgramsDataTable:0:j_id397
warn[17:18:24,545]: --- Enabling blur on statementProgramsDataTable:0:j_id397
Most of the time this loop then goes on for a variable number of iterations and then stops with the input field correctly focused. I've seen a few times where the loop appeared to be infinite.
It looks like IE is not respecting the absence of the onblur handler again? But only sometimes?!
So, this is where I'm confused. Why is IE behaving inconsistently? What am I doing wrong? Is there a better way?
Any ideas would be appreciated.
Val
EDIT:
I have tried the approach suggested by JeanK. It still does not work, but I learned something potentially useful from this attempt.
I wrap the original 'onblur' function with a proxy, like this:
this.allowBlur = false;
if ( !this._functionsEqual($(this.savedFocusId).onblur, this._proxiedBlur) ) {
$(this.savedFocusId).onblur = this.proxyOnBlur($(this.savedFocusId).onblur);
}
And the proxy function itself:
proxyOnBlur : function(method) {
var object = this;
this._proxiedBlur = function(event) {
if ( object.allowBlur ) {
return method.apply(this, [event || window.event])
}
};
return this._proxiedBlur;
},
Now, all I have to do is set this.allowBlur as the very last thing, and I should be fine. And herein lies the problem. In all other browsers, the onblur event is fired as I modify the DOM tree, so it correctly gets blocked. But in IE6, no matter what I do, it fires the onblur event after all of my code. I even tried this:
setTimeout(function() {
this.allowBlur = true;
}.bind(this), 2)
and my this._proxiedBlur still gets called after I set the flag to true!
So, the question becomes - Where do I need to put my flag reset so that it executes after IE6 reacts to the DOM changes?

ok, I figured it out. The trick was to set the focus in the separate thread:
setTimeout(function() {
this.setFocusAndRestoreBlur(req);
}.bind(this), 0)
and then reset the flag with another nested setTimeout(). This seems to sequence the events properly in IE6:
setFocusAndRestoreBlur : function(req) {
setFocus(req);
setTimeout(function() {
this.allowBlur = true;
}.bind(this), 0)
}

I suggest that you disable blur in a logic way.
I mean, something like this:
Suppose that this is your onblur event function, you can control it by flags:
function myOnBlur() {
if (ableToExecute) {
executeWhatYouNeed();
}
}
So, when you do your request, you set the flag to false, and then after the response and your needed processing, you reset the flat to true, so your onblur event can trigger your request again.
I don't know if this simple control can help you, but you can try it.

Related

Angular - Focus event being called on lost focus

I've come across some kind of bug I believe with angular and chrome and I am not quite sure what the solution is, my angular application has custom input controls and these inputs do some stuff on focus (focus)="someEvent($event). These inputs are the username and password field so chrome stores the values. Upon loading the page again, chrome will apply the stored values, if a user clicks elsewhere on the screen (NOT on the input components), both of the input components fire the focus event.
I could understand if this happened on page load as chrome may cycle through the inputs and apply the stored values, however this happens after the first mouse click anywhere on the page.
Is there a way to interpret that these focus events were cause by the autofill feature and not the user focusing on the input manually?
I have some code on these events that do event.target.select() to select all text, and oddly enough.. the 2 inputs end up getting stuck in a focus loop. The first gets focused then the second then the first then the second forever until a user presses tab.
HTML:
<input [ngClass]="inputClass" [type]="this.type" [readOnly]="this.readonly || (this.ParentPanel && this.ParentPanel.readonly)" [ngStyle]="inputStyle" [disabled]="disabled || (this.ParentPanel && this.ParentPanel.disabled)" [(ngModel)]="value" (change)="Event_change($event)" (keyup)="Event_keyup($event)" (keydown)="Event_keydown($event)" (focus)="Event_focus($event)" maxlength="512"/>
TS:
Event_focus(event) {
console.log('focus event' , event);
if (this.selectAllOnFocus) {
setTimeout(() => { // required to work with Edge (OnFocus happens before some browser properties are set)
event.target.select();
});
}
this.OnFocus.emit(event);
}
Thanks.
I figured this out, Before the code gets called - I have it check whether or not the control is the documents activeElement.
if (this.selectAllOnFocus && this.element && this.element.nativeElement === document.activeElement) { //do rest here }
These appears to work and resolve my issue.

Fire event with right mouse click and Paste

I want to fire an event in a textarea immediately after paste some text inside the textarea. I can do that when Shift+Ins is used; however, I cannot do it when right mouse button and then paste (from the drop down menu) is chosen. Keyup fires after Shift+Ins. None of the rest fires when Paste is chosen after right mouse button clicking... What do I have to do?
<textarea name="message" id="message"></textarea>
$("#message").on('keyup contextmenu', function(event) {
alert("ok");
});
http://jsfiddle.net/f29vuwoL/7/
Thank you
Most browsers support the input event, which is fired when something is pasted or otherwise added, regardless of how:
$("#message").on('keyup contextmenu input', function(event) {
alert("ok");
});
Updated Fiddle
Note that using input is the most general method, firing when the control gets input regardless of how, and so if you hook multiple events (as above), you'll get multiple calls for the same input. For instance, if you hook both keyup and input, on browsers that support input, you'll get two calls. Similarly for paste and input when the user pastes, on browsers that support both.
If you need to support browsers that don't have either input or paste, I'm afraid the unfortunate answer is that you need to poll. Still, polling every (say) 250ms isn't asking the browser to do that much work, and you can feature-detect whether it's necessary:
var message = $("#message");
var events = null;
var previous;
if ('oninput' in message[0]) {
// Browser supports input event
events = "input";
} else if ('onpaste' in message[0]) {
// Browser supports paste event
events = "paste keyup contextmenu";
}
if (!events) {
// Ugh, poll and fire our own
events = "pseudoinput";
previous = message.val();
setInterval(function() {
var current = message.val();
if (current != previous) {
previous = current;
message.trigger(events);
}
}, 250);
}
console.log("Using: " + events);
message.on(events, function(e) {
console.log("Got event: " + e.type);
});
Updated Fiddle
You should use input event callback. See the demo here
You can use the dedicated paste event:
$("#message").on('paste', function(event) {
alert("ok");
});
Updated jsFiddle
However you might want to check browser support - I don't think jQuery normalizes this event.
If you need IE support, it might be a little more difficult, but it depends on your requirements - does it absolutely need to be a paste action? If not, TJ Crowder's answer is the way to go.
The on input is useful if you want to detect when the contents of a textarea, input:text, input:password or input:search element have changed, because the onchange event on these elements fires when the element loses focus, not immediately after the modification.The oninput event is supported in Internet Explorer from version 9.
$("#message").on('input propertychange', function() {
console.log($(this).val());
});
Fiddle

JavaScript / DOM: how to prevent blur event if focus is lost to another window (application)

I want to show certain content within an HTML document if the user clicks into a certain form field within this document, and I want to hide that certain content if the user leaves that form field (either by activating another one or by clicking somewhere else onto the page).
I have tried to implement that behavior using the focus and blur events. This works in principle, but there is a problem: The blur event on the respective field is fired not only when the user moves the focus within the same document, but as well when another window (which could be from a different application) becomes activated (gets focus).
How could I avoid that? I don't want to see any changes in the page if the focus goes to a different application (or another browser window or tab).
Thank you very much!
Guard the blur event handler with if ( document.activeElement === this ) { return; }.
The next step will be to prevent the focus event handler from activating when the window regains focus. This can be done using a small pattern:
function onFocus(e) {
if ( this._isFocused ) { return; }
this._isFocused = true;
...
}
function onBlur(e) {
if ( document.activeElement === this ) { return; }
this._isFocused = false;
...
}
Maybe this could work:
function onFocus(element) {
document.getElementById('element').doStuffHere('whateverYouWant');
}
function onBlur(element) {
if (document.hasFocus()) {
document.getElementById('element').doStuffHere('whateverYouWant');
} else {
alert('Please come back!')
}
}
The onBlur() function is executed as soon as the element loses focus and first checks if the document still has the focus. If yes it does the elementLostFocus tasks (I'll call them like that here to make it easy), otherwise it (at least in this example) alerts the user, or you can make it do nothing or just the same elementLostFocus tasks or anthing you want.
The only problem with that solution is that you don't do the elementLostFocus tasks when the window regains focus by clicking outside the desired element after it lost the focus directly from the desired element to another window. But here's a fix for that:
document.onfocus = function() {
if (document.getElementById('element').hasFocus() == false) {
document.getElementById('element').doStuffHere('whateverYouWant');
}
}
It can be that that code doesn't work but it should. At least it should give you an idea based on which you can solve the problem yourself.

Jquery : how to trigger an event when the user clear a textbox

i have a function that currently working on .keypress event when the user right something in the textbox it do some code, but i want the same event to be triggered also when the user clear the textbox .change doesn't help since it fires after the user change the focus to something else
Thanks
The keyup event will detect if the user has cleared the box as well (i.e. backspace raises the event but backspace does not raise the keypress event in IE)
$("#inputname").keyup(function() {
if (!this.value) {
alert('The box is empty');
}
});
jsFiddle
As Josh says, this gets fired for every character code that is pressed in the input. This is mostly just showing that you need to use the keyup event to trigger backspace, rather than the keypress event you are currently using.
The solution by Jonathon Bolster does not cover all cases. I adapted it to also cover modifications by cutting and pasting:
$("#inputname").on('change keyup copy paste cut', function() {
//!this.value ...
});
see http://jsfiddle.net/gonfidentschal/XxLq2/
Unfortunately it's not possible to catch the cases where the field's value is set using javascript. If you set the value yourself it's not an issue because you know when you do it... but when you're using a library such as AngularJS that updates the view when the state changes then it can be a bit more work. Or you have to use a timer to check the value.
Also see the answer for Detecting input change in jQuery? which suggests the 'input' event understood by modern browsers. So just:
$("#inputname").on('input', function() {
//!this.value ...
});
Another way that does this in a concise manner is listening for "input" event on textarea/input-type:text fields
/**
* Listens on textarea input.
* Considers: undo, cut, paste, backspc, keyboard input, etc
*/
$("#myContainer").on("input", "textarea", function() {
if (!this.value) {
}
});
You can check the value of the input field inside the on input' function() and combine it with an if/else statement and it will work very well as in the code below :
$( "#myinputid" ).on('input', function() {
if($(this).val() != "") {
//Do action here like in this example am hiding the previous table row
$(this).closest("tr").prev("tr").hide(); //hides previous row
}else{
$(this).closest("tr").prev("tr").show(); //shows previous row
}
});
Inside your .keypress or .keyup function, check to see if the value of the input is empty. For example:
$("#some-input").keyup(function(){
if($(this).val() == "") {
// input is cleared
}
});
<input type="text" id="some-input" />

Preventing focus on next form element after showing alert using JQuery

I have some text inputs which I'm validating when a user tabs to the next one. I would like the focus to stay on a problematic input after showing an alert. I can't seem to nail down the correct syntax to have JQuery do this. Instead the following code shows the alert then focuses on the next text input. How can I prevent tabbing to the next element after showing an alert?
$('input.IosOverrideTextBox').bind({
blur: function(e) {
var val = $(this).val();
if (val.length == 0) return;
var pval = parseTicks(val);
if (isNaN(pval) || pval == 0.0) {
alert("Invalid override: " + val);
return false;
}
},
focus: function() {
$(this).select();
}
});
I don't like forced focus, but can't you just focus after the blur takes place?
element.focus();
If doing that in the blur event doesn't always work (I'm not sure exactly when it fires, before or after the actual blur takes place), a redundant timeout will do, as well: setTimeout(function () { element.focus() }, 0).
But please don't do this. Heck, you should never be using alert or any kind of modal dialog for a web interface, either. How about adding a invalid class to the form field, putting a message off to the side of it, and disabling submit until all fields are valid? That's a much less invasive solution that allows me to fill out the form in whatever way is best for me, rather than whatever way is simplest for you.
You can do this with the validation plugin by default.
focusInvalid default: true
Focus the last active or first invalid element on submit via validator.focusInvalid(). The last active element is the one that had focus when the form was submitted, avoiding to steal its focus. If there was no element focused, the first one in the form gets it, unless this option is turned off.
Then you'd only need to have the focus event handler do your select and let the plugin handle validation.

Categories

Resources