Safari doesn't correctly change active element on focus - javascript

I have a tooltip which depends on becoming the active element when clicked, so that on blur it can hide.
In a decent browser like Chrome a <button> becomes the active element when clicked. In FF and Safari it doesn't (they don't even call focus on the element!).
So I switched to an <a>, but even that is still broken in Safari.
Here is a demo of the issue, try in Chrome and then Safari:
document.querySelector('a').addEventListener('click', () => {
echoActiveEl();
});
function echoActiveEl() {
document.querySelector('.active-el-tag').innerHTML = document.activeElement.tagName.toLowerCase();
}
echoActiveEl();
Click me
<p>Active element: <span class="active-el-tag"></span></p>
How can I make Safari behave properly and call focus on the element so it becomes the activeElement? Doing element.focus() does nothing! Thanks.
Edit: e.currentTarget.focus() does actually work, e.target was pointing to a span I had in my <a>.

As far as manually putting focus is concerned i think it's an ES 6 support issue , try traditional syntax instead
document.querySelector('a').addEventListener('click', function(e) {
//e.preventDefault();
this.focus();
document.querySelector('.active-el-tag').innerHTML = document.activeElement.tagName.toLowerCase();
});

Since you need the onBlur event, you can archive what you want with something like this:
document.querySelector('a').addEventListener('blur', () => {
var targetElement = event.target || event.srcElement;
echoBluredEl(targetElement)
});
function echoBluredEl(ele) {
document.querySelector('.blured-el-tag').innerHTML = ele.tagName.toLowerCase();
}

Related

how to check if the textbox is in focus in Javascript [duplicate]

I would like to find out, in JavaScript, which element currently has focus. I've been looking through the DOM and haven't found what I need, yet. Is there a way to do this, and how?
The reason I was looking for this:
I'm trying to make keys like the arrows and enter navigate through a table of input elements. Tab works now, but enter, and arrows do not by default it seems. I've got the key handling part set up but now I need to figure out how to move the focus over in the event handling functions.
Use document.activeElement, it is supported in all major browsers.
Previously, if you were trying to find out what form field has focus, you could not. To emulate detection within older browsers, add a "focus" event handler to all fields and record the last-focused field in a variable. Add a "blur" handler to clear the variable upon a blur event for the last-focused field.
If you need to remove the activeElement you can use blur; document.activeElement.blur(). It will change the activeElement to body.
Related links:
activeElement Browser Compatibility
jQuery alternative for document.activeElement
As said by JW, you can't find the current focused element, at least in a browser-independent way. But if your app is IE only (some are...), you can find it the following way:
document.activeElement
It looks like IE did not have everything wrong after all, this is part of HTML5 draft and seems to be supported by the latest version of Chrome, Safari and Firefox at least.
If you can use jQuery, it now supports :focus, just make sure you are using version 1.6+.
This statement will get you the currently focused element.
$(":focus")
From: How to select an element that has focus on it with jQuery
document.activeElement is now part of the HTML5 working draft specification, but it might not yet be supported in some non-major/mobile/older browsers. You can fall back to querySelector (if that is supported). It's also worth mentioning that document.activeElement will return document.body if no element is focused — even if the browser window doesn't have focus.
The following code will work around this issue and fall back to querySelector giving a little better support.
var focused = document.activeElement;
if (!focused || focused == document.body)
focused = null;
else if (document.querySelector)
focused = document.querySelector(":focus");
An addition thing to note is the performance difference between these two methods. Querying the document with selectors will always be much slower than accessing the activeElement property. See this jsperf.com test.
By itself, document.activeElement can still return an element if the document isn't focused (and thus nothing in the document is focused!)
You may want that behavior, or it may not matter (e.g. within a keydown event), but if you need to know something is actually focused, you can additionally check document.hasFocus().
The following will give you the focused element if there is one, or else null.
var focused_element = null;
if (
document.hasFocus() &&
document.activeElement !== document.body &&
document.activeElement !== document.documentElement
) {
focused_element = document.activeElement;
}
To check whether a specific element has focus, it's simpler:
var input_focused = document.activeElement === input && document.hasFocus();
To check whether anything is focused, it's more complex again:
var anything_is_focused = (
document.hasFocus() &&
document.activeElement !== null &&
document.activeElement !== document.body &&
document.activeElement !== document.documentElement
);
Robustness Note: In the code where it the checks against document.body and document.documentElement, this is because some browsers return one of these or null when nothing is focused.
It doesn't account for if the <body> (or maybe <html>) had a tabIndex attribute and thus could actually be focused. If you're writing a library or something and want it to be robust, you should probably handle that somehow.
Here's a (heavy airquotes) "one-liner" version of getting the focused element, which is conceptually more complicated because you have to know about short-circuiting, and y'know, it obviously doesn't fit on one line, assuming you want it to be readable.
I'm not gonna recommend this one. But if you're a 1337 hax0r, idk... it's there.
You could also remove the || null part if you don't mind getting false in some cases. (You could still get null if document.activeElement is null):
var focused_element = (
document.hasFocus() &&
document.activeElement !== document.body &&
document.activeElement !== document.documentElement &&
document.activeElement
) || null;
For checking if a specific element is focused, alternatively you could use events, but this way requires setup (and potentially teardown), and importantly, assumes an initial state:
var input_focused = false;
input.addEventListener("focus", function() {
input_focused = true;
});
input.addEventListener("blur", function() {
input_focused = false;
});
You could fix the initial state assumption by using the non-evented way, but then you might as well just use that instead.
document.activeElement may default to the <body> element if no focusable elements are in focus. Additionally, if an element is focused and the browser window is blurred, activeElement will continue to hold the focused element.
If either of these two behaviors are not desirable, consider a CSS-based approach: document.querySelector( ':focus' ).
I have found the following snippet to be useful when trying to determine which element currently has focus. Copy the following into the console of your browser, and every second it will print out the details of the current element that has focus.
setInterval(function() { console.log(document.querySelector(":focus")); }, 1000);
Feel free to modify the console.log to log out something different to help you pinpoint the exact element if printing out the whole element does not help you pinpoint the element.
I liked the approach used by Joel S, but I also love the simplicity of document.activeElement. I used jQuery and combined the two. Older browsers that don't support document.activeElement will use jQuery.data() to store the value of 'hasFocus'. Newer browsers will use document.activeElement. I assume that document.activeElement will have better performance.
(function($) {
var settings;
$.fn.focusTracker = function(options) {
settings = $.extend({}, $.focusTracker.defaults, options);
if (!document.activeElement) {
this.each(function() {
var $this = $(this).data('hasFocus', false);
$this.focus(function(event) {
$this.data('hasFocus', true);
});
$this.blur(function(event) {
$this.data('hasFocus', false);
});
});
}
return this;
};
$.fn.hasFocus = function() {
if (this.length === 0) { return false; }
if (document.activeElement) {
return this.get(0) === document.activeElement;
}
return this.data('hasFocus');
};
$.focusTracker = {
defaults: {
context: 'body'
},
focusedElement: function(context) {
var focused;
if (!context) { context = settings.context; }
if (document.activeElement) {
if ($(document.activeElement).closest(context).length > 0) {
focused = document.activeElement;
}
} else {
$(':visible:enabled', context).each(function() {
if ($(this).data('hasFocus')) {
focused = this;
return false;
}
});
}
return $(focused);
}
};
})(jQuery);
A little helper that I've used for these purposes in Mootools:
FocusTracker = {
startFocusTracking: function() {
this.store('hasFocus', false);
this.addEvent('focus', function() { this.store('hasFocus', true); });
this.addEvent('blur', function() { this.store('hasFocus', false); });
},
hasFocus: function() {
return this.retrieve('hasFocus');
}
}
Element.implement(FocusTracker);
This way you can check if element has focus with el.hasFocus() provided that startFocusTracking() has been called on the given element.
JQuery does support the :focus pseudo-class as of current. If you are looking for it in the JQuery documentation, check under "Selectors" where it points you to the W3C CSS docs. I've tested with Chrome, FF, and IE 7+. Note that for it to work in IE, <!DOCTYPE... must exist on the html page. Here is an example assuming you've assigned an id to the element that has focus:
$(":focus").each(function() {
alert($(this).attr("id") + " has focus!");
});
If you want to get a object that is instance of Element, you must use document.activeElement, but if you want to get a object that is instance of Text, you must to use document.getSelection().focusNode.
I hope helps.
If you're using jQuery, you can use this to find out if an element is active:
$("input#id").is(":active");
There are potential problems with using document.activeElement. Consider:
<div contentEditable="true">
<div>Some text</div>
<div>Some text</div>
<div>Some text</div>
</div>
If the user focuses on an inner-div, then document.activeElement still references the outer div. You cannot use document.activeElement to determine which of the inner div's has focus.
The following function gets around this, and returns the focused node:
function active_node(){
return window.getSelection().anchorNode;
}
If you would rather get the focused element, use:
function active_element(){
var anchor = window.getSelection().anchorNode;
if(anchor.nodeType == 3){
return anchor.parentNode;
}else if(anchor.nodeType == 1){
return anchor;
}
}
Reading other answers, and trying myself, it seems document.activeElement will give you the element you need in most browsers.
If you have a browser that doesn't support document.activeElement if you have jQuery around, you should be able populate it on all focus events with something very simple like this (untested as I don't have a browser meeting those criteria to hand):
if (typeof document.activeElement === 'undefined') { // Check browser doesn't do it anyway
$('*').live('focus', function () { // Attach to all focus events using .live()
document.activeElement = this; // Set activeElement to the element that has been focussed
});
}
With dojo, you can use dijit.getFocus()
Just putting this here to give the solution I eventually came up with.
I created a property called document.activeInputArea, and used jQuery's HotKeys addon to trap keyboard events for arrow keys, tab and enter, and I created an event handler for clicking into input elements.
Then I adjusted the activeInputArea every time focus changed, so I could use that property to find out where I was.
It's easy to screw this up though, because if you have a bug in the system and focus isn't where you think it is, then its very hard to restore the correct focus.
simple use document.activeElement to find the current active element
use document.activeElement.id
appending .id filters out returning the entire DOM and allows you to work only with identified elements
If you want to test the focused element on the dev tools, I suggest using.
$(":focus")
As document.activeElement will change to body when you click on anything in the dev tool.
To get the previous active element add this.
Example: you click on a button and need the previous active element. Since the button gets the focus once click on it.
document.addEventListener("focusout",ev => {
document.previousActiveElement = ev.target;
});

Firefox firing dragleave when dragging over text

I'm attempting to track a dragenter/leave for the entire screen, which is so far working fine in Chrome/Safari, courtesy of the draghover plugin from https://stackoverflow.com/a/10310815/698289 as in:
$.fn.draghover = function(options) {
return this.each(function() {
var collection = $(),
self = $(this);
self.on('dragenter', function(e) {
if (collection.size() === 0) {
self.trigger('draghoverstart');
}
collection = collection.add(e.target);
});
self.on('dragleave drop', function(e) {
// timeout is needed because Firefox 3.6 fires the dragleave event on
// the previous element before firing dragenter on the next one
setTimeout( function() {
collection = collection.not(e.target);
if (collection.size() === 0) {
self.trigger('draghoverend');
}
}, 1);
});
});
};
function setText(text) {
$('p.target').text(text);
}
$(document).ready(function() {
$(window).draghover().on({
'draghoverstart': function() {
setText('enter');
},
'draghoverend': function() {
setText('leave');
}
});
});
However Firefox is still giving me problems when I drag over text items, here's a fiddle to demonstrate: http://jsfiddle.net/tusRy/6/
Is this a Firefox bug or can this be tamed with JS? Or is there a more robust method for performing all of this?
Thanks!
UPDATE: Updated fiddle to http://jsfiddle.net/tusRy/6/ to reduce clutter a bit. To explain the expected behavior of the fiddle:
Drag a file into the window and p.target should be "ENTER" colored yellow.
Drag a file out of the window and p.target should be "LEAVE" colored red.
Drop a file in the window and p.target should be "LEAVE" colored red.
In firefox, the LEAVE event is triggered when you drag the file over text.
As of version 22.0 Firefox is still doing this. When you drag over a text node it fires two kinds of dragenter and dragleave events: one where the event target and relatedTarget are BOTH the parent element of the text node, and another where the target is the parent element and the relatedTarget is the actual text node (not even a proper DOM element).
The workaround is just to check for those two kinds of events in your dragenter and dragleave handlers and ignore them:
try {
if(event.relatedTarget.nodeType == 3) return;
} catch(err) {}
if(event.target === event.relatedTarget) return;
I use a try/catch block to check the nodeType because occasionally events fire (inexplicably) from outside the document (eg. in other iframes) and trying to access their nodeType throws a permissions error.
Here's the implementation:
http://jsfiddle.net/9A7te/
1) Your dropzone should have only one child element, which might have everything else you need. Something like
<div id="#dropzone">
<div><!--Your contents here--></div>
</div>
2) Use this CSS:
#dropzone * { pointer-events: none; }
You might need to include the :before and :after since the * don't apply to them.
This should be enough to let the drop work in Firefox and Chrome. In your example, it should be enough to add:
body * { pointer-events: none; }
At the end of the CSS. I've done it here:
http://jsfiddle.net/djsbellini/eKttq/
Other examples:
http://jsfiddle.net/djsbellini/6yZV6/1/
http://jsfiddle.net/djsbellini/yR8t8/
I came up with kind of a solution, yet to test on other browsers other than Chrome and FF but working so far. This is how the setTimeout looks now:
setTimeout( function() {
var isChild = false;
// in order to get permission errors, use the try-catch
// to check if the relatedTarget is a child of the body
try {
isChild = $('body').find(e.relatedTarget).length ? true : isChild;
}
catch(err){} // do nothing
collection = collection.not(e.target);
if (collection.size() === 0 && !isChild) {
self.trigger('draghoverend');
}
}, 1);
The entire code here - http://jsfiddle.net/tusRy/13/.
The idea is to check if the related tag is a child of the body, in which case we are still in the Browsers and the draghoverend event should be not triggered. As this can throw errors when moving out of the windows, I used a try method to avoid it.
Well, perhaps somebody with more skills on JS could improve this :)
I found the answer in a non-selected answer to this SO question asking about dragleave firing on child elements. I have a <div> that has many children elements. An semi-opaque overlay <span> becomes visible over the <div> whenever there's a dragenter on the page. As you found, 'dragover' isn't like mouseover. It triggers dragleave whenever you hover over a child element.
The solution? Dragout It makes dragover work more like mouseover. Very short.

how to use Jquery :focus selector on input elements in iOS?

I'm using Jquery 1.7.1 and iPad1 iOS3.
I need to fire a function on scrollstart unless an input element has focus. The function below worked for a long time, but all of a sudden it doesn't (looking aroud I guess because of switching to Jquery 1.7.1)
$(document).on('scroll', function(){
if ( !$("input:focus").length > 0 ) {
self.hideAllPanels();
}
});
Specifically $('input:focus').length = 0, although I can detect the focus event triggering before the scroll event.
I have been fiddling with a workaround using:
if ( !$(document.activeElement).get(0).tagName == "input" ){
....
}
But I'm not sure when the activeElement is changing, because it seems to persist for quite a while even after I'm "leaving" the resprective element.
Question:
Any idea why I can't detect the focus-ed element on iOS? Some hints how I could set this up with activeElement, so on blur, I'm no longer having the input as activeElement are also welcome!
Thanks for help!
$(document).on('scroll', function(){
if (!$("input").is(':focus')) {
self.hideAllPanels();
}
});
This seems to work for me, but it will fire the function multiple times during scroll if an input element does not have focus.

How do I find out which DOM element has the focus?

I would like to find out, in JavaScript, which element currently has focus. I've been looking through the DOM and haven't found what I need, yet. Is there a way to do this, and how?
The reason I was looking for this:
I'm trying to make keys like the arrows and enter navigate through a table of input elements. Tab works now, but enter, and arrows do not by default it seems. I've got the key handling part set up but now I need to figure out how to move the focus over in the event handling functions.
Use document.activeElement, it is supported in all major browsers.
Previously, if you were trying to find out what form field has focus, you could not. To emulate detection within older browsers, add a "focus" event handler to all fields and record the last-focused field in a variable. Add a "blur" handler to clear the variable upon a blur event for the last-focused field.
If you need to remove the activeElement you can use blur; document.activeElement.blur(). It will change the activeElement to body.
Related links:
activeElement Browser Compatibility
jQuery alternative for document.activeElement
As said by JW, you can't find the current focused element, at least in a browser-independent way. But if your app is IE only (some are...), you can find it the following way:
document.activeElement
It looks like IE did not have everything wrong after all, this is part of HTML5 draft and seems to be supported by the latest version of Chrome, Safari and Firefox at least.
If you can use jQuery, it now supports :focus, just make sure you are using version 1.6+.
This statement will get you the currently focused element.
$(":focus")
From: How to select an element that has focus on it with jQuery
document.activeElement is now part of the HTML5 working draft specification, but it might not yet be supported in some non-major/mobile/older browsers. You can fall back to querySelector (if that is supported). It's also worth mentioning that document.activeElement will return document.body if no element is focused — even if the browser window doesn't have focus.
The following code will work around this issue and fall back to querySelector giving a little better support.
var focused = document.activeElement;
if (!focused || focused == document.body)
focused = null;
else if (document.querySelector)
focused = document.querySelector(":focus");
An addition thing to note is the performance difference between these two methods. Querying the document with selectors will always be much slower than accessing the activeElement property. See this jsperf.com test.
By itself, document.activeElement can still return an element if the document isn't focused (and thus nothing in the document is focused!)
You may want that behavior, or it may not matter (e.g. within a keydown event), but if you need to know something is actually focused, you can additionally check document.hasFocus().
The following will give you the focused element if there is one, or else null.
var focused_element = null;
if (
document.hasFocus() &&
document.activeElement !== document.body &&
document.activeElement !== document.documentElement
) {
focused_element = document.activeElement;
}
To check whether a specific element has focus, it's simpler:
var input_focused = document.activeElement === input && document.hasFocus();
To check whether anything is focused, it's more complex again:
var anything_is_focused = (
document.hasFocus() &&
document.activeElement !== null &&
document.activeElement !== document.body &&
document.activeElement !== document.documentElement
);
Robustness Note: In the code where it the checks against document.body and document.documentElement, this is because some browsers return one of these or null when nothing is focused.
It doesn't account for if the <body> (or maybe <html>) had a tabIndex attribute and thus could actually be focused. If you're writing a library or something and want it to be robust, you should probably handle that somehow.
Here's a (heavy airquotes) "one-liner" version of getting the focused element, which is conceptually more complicated because you have to know about short-circuiting, and y'know, it obviously doesn't fit on one line, assuming you want it to be readable.
I'm not gonna recommend this one. But if you're a 1337 hax0r, idk... it's there.
You could also remove the || null part if you don't mind getting false in some cases. (You could still get null if document.activeElement is null):
var focused_element = (
document.hasFocus() &&
document.activeElement !== document.body &&
document.activeElement !== document.documentElement &&
document.activeElement
) || null;
For checking if a specific element is focused, alternatively you could use events, but this way requires setup (and potentially teardown), and importantly, assumes an initial state:
var input_focused = false;
input.addEventListener("focus", function() {
input_focused = true;
});
input.addEventListener("blur", function() {
input_focused = false;
});
You could fix the initial state assumption by using the non-evented way, but then you might as well just use that instead.
document.activeElement may default to the <body> element if no focusable elements are in focus. Additionally, if an element is focused and the browser window is blurred, activeElement will continue to hold the focused element.
If either of these two behaviors are not desirable, consider a CSS-based approach: document.querySelector( ':focus' ).
I have found the following snippet to be useful when trying to determine which element currently has focus. Copy the following into the console of your browser, and every second it will print out the details of the current element that has focus.
setInterval(function() { console.log(document.querySelector(":focus")); }, 1000);
Feel free to modify the console.log to log out something different to help you pinpoint the exact element if printing out the whole element does not help you pinpoint the element.
I liked the approach used by Joel S, but I also love the simplicity of document.activeElement. I used jQuery and combined the two. Older browsers that don't support document.activeElement will use jQuery.data() to store the value of 'hasFocus'. Newer browsers will use document.activeElement. I assume that document.activeElement will have better performance.
(function($) {
var settings;
$.fn.focusTracker = function(options) {
settings = $.extend({}, $.focusTracker.defaults, options);
if (!document.activeElement) {
this.each(function() {
var $this = $(this).data('hasFocus', false);
$this.focus(function(event) {
$this.data('hasFocus', true);
});
$this.blur(function(event) {
$this.data('hasFocus', false);
});
});
}
return this;
};
$.fn.hasFocus = function() {
if (this.length === 0) { return false; }
if (document.activeElement) {
return this.get(0) === document.activeElement;
}
return this.data('hasFocus');
};
$.focusTracker = {
defaults: {
context: 'body'
},
focusedElement: function(context) {
var focused;
if (!context) { context = settings.context; }
if (document.activeElement) {
if ($(document.activeElement).closest(context).length > 0) {
focused = document.activeElement;
}
} else {
$(':visible:enabled', context).each(function() {
if ($(this).data('hasFocus')) {
focused = this;
return false;
}
});
}
return $(focused);
}
};
})(jQuery);
A little helper that I've used for these purposes in Mootools:
FocusTracker = {
startFocusTracking: function() {
this.store('hasFocus', false);
this.addEvent('focus', function() { this.store('hasFocus', true); });
this.addEvent('blur', function() { this.store('hasFocus', false); });
},
hasFocus: function() {
return this.retrieve('hasFocus');
}
}
Element.implement(FocusTracker);
This way you can check if element has focus with el.hasFocus() provided that startFocusTracking() has been called on the given element.
JQuery does support the :focus pseudo-class as of current. If you are looking for it in the JQuery documentation, check under "Selectors" where it points you to the W3C CSS docs. I've tested with Chrome, FF, and IE 7+. Note that for it to work in IE, <!DOCTYPE... must exist on the html page. Here is an example assuming you've assigned an id to the element that has focus:
$(":focus").each(function() {
alert($(this).attr("id") + " has focus!");
});
If you want to get a object that is instance of Element, you must use document.activeElement, but if you want to get a object that is instance of Text, you must to use document.getSelection().focusNode.
I hope helps.
If you're using jQuery, you can use this to find out if an element is active:
$("input#id").is(":active");
There are potential problems with using document.activeElement. Consider:
<div contentEditable="true">
<div>Some text</div>
<div>Some text</div>
<div>Some text</div>
</div>
If the user focuses on an inner-div, then document.activeElement still references the outer div. You cannot use document.activeElement to determine which of the inner div's has focus.
The following function gets around this, and returns the focused node:
function active_node(){
return window.getSelection().anchorNode;
}
If you would rather get the focused element, use:
function active_element(){
var anchor = window.getSelection().anchorNode;
if(anchor.nodeType == 3){
return anchor.parentNode;
}else if(anchor.nodeType == 1){
return anchor;
}
}
Reading other answers, and trying myself, it seems document.activeElement will give you the element you need in most browsers.
If you have a browser that doesn't support document.activeElement if you have jQuery around, you should be able populate it on all focus events with something very simple like this (untested as I don't have a browser meeting those criteria to hand):
if (typeof document.activeElement === 'undefined') { // Check browser doesn't do it anyway
$('*').live('focus', function () { // Attach to all focus events using .live()
document.activeElement = this; // Set activeElement to the element that has been focussed
});
}
With dojo, you can use dijit.getFocus()
Just putting this here to give the solution I eventually came up with.
I created a property called document.activeInputArea, and used jQuery's HotKeys addon to trap keyboard events for arrow keys, tab and enter, and I created an event handler for clicking into input elements.
Then I adjusted the activeInputArea every time focus changed, so I could use that property to find out where I was.
It's easy to screw this up though, because if you have a bug in the system and focus isn't where you think it is, then its very hard to restore the correct focus.
simple use document.activeElement to find the current active element
use document.activeElement.id
appending .id filters out returning the entire DOM and allows you to work only with identified elements
If you want to test the focused element on the dev tools, I suggest using.
$(":focus")
As document.activeElement will change to body when you click on anything in the dev tool.
To get the previous active element add this.
Example: you click on a button and need the previous active element. Since the button gets the focus once click on it.
document.addEventListener("focusout",ev => {
document.previousActiveElement = ev.target;
});

When a 'blur' event occurs, how can I find out which element focus went *to*?

Suppose I attach an blur function to an HTML input box like this:
<input id="myInput" onblur="function() { ... }"></input>
Is there a way to get the ID of the element which caused the blur event to fire (the element which was clicked) inside the function? How?
For example, suppose I have a span like this:
<span id="mySpan">Hello World</span>
If I click the span right after the input element has focus, the input element will lose its focus. How does the function know that it was mySpan that was clicked?
PS: If the onclick event of the span would occur before the onblur event of the input element my problem would be solved, because I could set some status value indicating a specific element had been clicked.
PPS: The background of this problem is that I want to trigger an AJAX autocompleter control externally (from a clickable element) to show its suggestions, without the suggestions disappearing immediately because of the blur event on the input element. So I want to check in the blur function if one specific element has been clicked, and if so, ignore the blur event.
2015 answer: according to UI Events, you can use the relatedTarget property of the event:
Used to identify a secondary EventTarget related to a Focus
event, depending on the type of event.
For blur events,
relatedTarget: event target receiving focus.
Example:
function blurListener(event) {
event.target.className = 'blurred';
if(event.relatedTarget)
event.relatedTarget.className = 'focused';
}
[].forEach.call(document.querySelectorAll('input'), function(el) {
el.addEventListener('blur', blurListener, false);
});
.blurred { background: orange }
.focused { background: lime }
<p>Blurred elements will become orange.</p>
<p>Focused elements should become lime.</p>
<input /><input /><input />
Note Firefox won't support relatedTarget until version 48 (bug 962251, MDN).
Hmm... In Firefox, you can use explicitOriginalTarget to pull the element that was clicked on. I expected toElement to do the same for IE, but it does not appear to work... However, you can pull the newly-focused element from the document:
function showBlur(ev)
{
var target = ev.explicitOriginalTarget||document.activeElement;
document.getElementById("focused").value =
target ? target.id||target.tagName||target : '';
}
...
<button id="btn1" onblur="showBlur(event)">Button 1</button>
<button id="btn2" onblur="showBlur(event)">Button 2</button>
<button id="btn3" onblur="showBlur(event)">Button 3</button>
<input id="focused" type="text" disabled="disabled" />
Caveat: This technique does not work for focus changes caused by tabbing through fields with the keyboard, and does not work at all in Chrome or Safari. The big problem with using activeElement (except in IE) is that it is not consistently updated until after the blur event has been processed, and may have no valid value at all during processing! This can be mitigated with a variation on the technique Michiel ended up using:
function showBlur(ev)
{
// Use timeout to delay examination of activeElement until after blur/focus
// events have been processed.
setTimeout(function()
{
var target = document.activeElement;
document.getElementById("focused").value =
target ? target.id||target.tagName||target : '';
}, 1);
}
This should work in most modern browsers (tested in Chrome, IE, and Firefox), with the caveat that Chrome does not set focus on buttons that are clicked (vs. tabbed to).
I solved it eventually with a timeout on the onblur event (thanks to the advice of a friend who is not StackOverflow):
<input id="myInput" onblur="setTimeout(function() {alert(clickSrc);},200);"></input>
<span onclick="clickSrc='mySpan';" id="mySpan">Hello World</span>
Works both in FF and IE.
It's possible to use mousedown event of document instead of blur:
$(document).mousedown(function(){
if ($(event.target).attr("id") == "mySpan") {
// some process
}
});
The instance of type FocusEvent has the relatedTarget property, however, up to version 47 of the FF, specifically, this attribute returns null, from 48 it already works.
You can see more here.
Works in Google Chrome v66.x, Mozilla v59.x and Microsoft Edge... Solution with jQuery.
I test in Internet Explorer 9 and not supported.
$("#YourElement").blur(function(e){
var InputTarget = $(e.relatedTarget).attr("id"); // GET ID Element
console.log(InputTarget);
if(target == "YourId") { // If you want validate or make a action to specfic element
... // your code
}
});
Comment your test in others internet explorer versions.
I am also trying to make Autocompleter ignore blurring if a specific element clicked and have a working solution, but for only Firefox due to explicitOriginalTarget
Autocompleter.Base.prototype.onBlur = Autocompleter.Base.prototype.onBlur.wrap(
function(origfunc, ev) {
if ($(this.options.ignoreBlurEventElement)) {
var newTargetElement = (ev.explicitOriginalTarget.nodeType == 3 ? ev.explicitOriginalTarget.parentNode : ev.explicitOriginalTarget);
if (!newTargetElement.descendantOf($(this.options.ignoreBlurEventElement))) {
return origfunc(ev);
}
}
}
);
This code wraps default onBlur method of Autocompleter and checks if ignoreBlurEventElement parameters is set. if it is set, it checks everytime to see if clicked element is ignoreBlurEventElement or not. If it is, Autocompleter does not cal onBlur, else it calls onBlur. The only problem with this is that it only works in Firefox because explicitOriginalTarget property is Mozilla specific . Now I am trying to find a different way than using explicitOriginalTarget. The solution you have mentioned requires you to add onclick behaviour manually to the element. If I can't manage to solve explicitOriginalTarget issue, I guess I will follow your solution.
Can you reverse what you're checking and when? That is if you remeber what was blurred last:
<input id="myInput" onblur="lastBlurred=this;"></input>
and then in the onClick for your span, call function() with both objects:
<span id="mySpan" onClick="function(lastBlurred, this);">Hello World</span>
Your function could then decide whether or not to trigger the Ajax.AutoCompleter control. The function has the clicked object and the blurred object. The onBlur has already happened so it won't make the suggestions disappear.
Use something like this:
var myVar = null;
And then inside your function:
myVar = fldID;
And then:
setTimeout(setFocus,1000)
And then:
function setFocus(){ document.getElementById(fldID).focus(); }
Final code:
<html>
<head>
<script type="text/javascript">
function somefunction(){
var myVar = null;
myVar = document.getElementById('myInput');
if(myVar.value=='')
setTimeout(setFocusOnJobTitle,1000);
else
myVar.value='Success';
}
function setFocusOnJobTitle(){
document.getElementById('myInput').focus();
}
</script>
</head>
<body>
<label id="jobTitleId" for="myInput">Job Title</label>
<input id="myInput" onblur="somefunction();"></input>
</body>
</html>
i think it's not possibe,
with IE you can try to use window.event.toElement, but it dosn't work with firefox!
You can fix IE with :
event.currentTarget.firstChild.ownerDocument.activeElement
It looks like "explicitOriginalTarget" for FF.
Antoine And J
As noted in this answer, you can check the value of document.activeElement. document is a global variable, so you don't have to do any magic to use it in your onBlur handler:
function myOnBlur(e) {
if(document.activeElement ===
document.getElementById('elementToCheckForFocus')) {
// Focus went where we expected!
// ...
}
}
document.activeElement could be a parent node (for example body node because it is in a temporary phase switching from a target to another), so it is not usable for your scope
ev.explicitOriginalTarget is not always valued
So the best way is to use onclick on body event for understanding indirectly your node(event.target) is on blur
Edit:
A hacky way to do it would be to create a variable that keeps track of focus for every element you care about. So, if you care that 'myInput' lost focus, set a variable to it on focus.
<script type="text/javascript">
var lastFocusedElement;
</script>
<input id="myInput" onFocus="lastFocusedElement=this;" />
Original Answer:
You can pass 'this' to the function.
<input id="myInput" onblur="function(this){
var theId = this.id; // will be 'myInput'
}" />
I suggest using global variables blurfrom and blurto. Then, configure all elements you care about to assign their position in the DOM to the variable blurfrom when they lose focus. Additionally, configure them so that gaining focus sets the variable blurto to their position in the DOM. Then, you could use another function altogether to analyze the blurfrom and blurto data.
keep in mind, that the solution with explicitOriginalTarget does not work for text-input-to-text-input jumps.
try to replace buttons with the following text-inputs and you will see the difference:
<input id="btn1" onblur="showBlur(event)" value="text1">
<input id="btn2" onblur="showBlur(event)" value="text2">
<input id="btn3" onblur="showBlur(event)" value="text3">
I've been playing with this same feature and found out that FF, IE, Chrome and Opera have the ability to provide the source element of an event. I haven't tested Safari but my guess is it might have something similar.
$('#Form').keyup(function (e) {
var ctrl = null;
if (e.originalEvent.explicitOriginalTarget) { // FF
ctrl = e.originalEvent.explicitOriginalTarget;
}
else if (e.originalEvent.srcElement) { // IE, Chrome and Opera
ctrl = e.originalEvent.srcElement;
}
//...
});
I do not like using timeout when coding javascript so I would do it the opposite way of Michiel Borkent. (Did not try the code behind but you should get the idea).
<input id="myInput" onblur="blured = this.id;"></input>
<span onfocus = "sortOfCallback(this.id)" id="mySpan">Hello World</span>
In the head something like that
<head>
<script type="text/javascript">
function sortOfCallback(id){
bluredElement = document.getElementById(blured);
// Do whatever you want on the blured element with the id of the focus element
}
</script>
</head>
I wrote an alternative solution how to make any element focusable and "blurable".
It's based on making an element as contentEditable and hiding visually it and disabling edit mode itself:
el.addEventListener("keydown", function(e) {
e.preventDefault();
e.stopPropagation();
});
el.addEventListener("blur", cbBlur);
el.contentEditable = true;
DEMO
Note: Tested in Chrome, Firefox, and Safari (OS X). Not sure about IE.
Related: I was searching for a solution for VueJs, so for those who interested/curious how to implement such functionality using Vue Focusable directive, please take a look.
I see only hacks in the answers, but there's actually a builtin solution very easy to use :
Basically you can capture the focus element like this:
const focusedElement = document.activeElement
https://developer.mozilla.org/en-US/docs/Web/API/DocumentOrShadowRoot/activeElement
This way:
<script type="text/javascript">
function yourFunction(element) {
alert(element);
}
</script>
<input id="myinput" onblur="yourFunction(this)">
Or if you attach the listener via JavaScript (jQuery in this example):
var input = $('#myinput').blur(function() {
alert(this);
});
Edit: sorry. I misread the question.
I think its easily possible via jquery by passing the reference of the field causing the onblur event in "this".
For e.g.
<input type="text" id="text1" onblur="showMessageOnOnblur(this)">
function showMessageOnOnblur(field){
alert($(field).attr("id"));
}
Thanks
Monika
You could make it like this:
<script type="text/javascript">
function myFunction(thisElement)
{
document.getElementByName(thisElement)[0];
}
</script>
<input type="text" name="txtInput1" onBlur="myFunction(this.name)"/>

Categories

Resources