Retrieving previously focused element - javascript

I would like to find out, in Javascript, which previous element had focus as opposed to the current focus. I've been looking through the DOM and haven't found what I need, yet. Is there a way to do this any help would be much appreciated

Each time an element is focused, you'd have to store which one it was. Then when another element is focused, you could retrieve the variable for the previous focused element.
So basically, your single focus handler would do 2 things:
Check if previousFocus is defined. If it is, retrieve it.
Set previousFocus to the currently focused element.
Here is a quick demo with jQuery (you can use raw JS too... just fewer lines w jQuery, so it's easier to understand imo):
// create an anonymous function that we call immediately
// this will hold our previous focus variable, so we don't
// clutter the global scope
(function() {
// the variable to hold the previously focused element
var prevFocus;
// our single focus event handler
$("input").focus(function() {
// let's check if the previous focus has already been defined
if (typeof prevFocus !== "undefined") {
// we do something with the previously focused element
$("#prev").html(prevFocus.val());
}
// AFTER we check upon the previously focused element
// we (re)define the previously focused element
// for use in the next focus event
prevFocus = $(this);
});
})();
working jsFiddle

Just found this question while solving the exact same problem and realised it was so old the jQuery world has moved on a bit :)
This should provide a more effective version of Peter Ajtais code, as it will use only a single delegated event handler (not one per input element).
// prime with empty jQuery object
window.prevFocus = $();
// Catch any bubbling focusin events (focus does not bubble)
$(document).on('focusin', ':input', function () {
// Test: Show the previous value/text so we know it works!
$("#prev").html(prevFocus.val() || prevFocus.text());
// Save the previously clicked value for later
window.prevFocus = $(this);
});
JSFiddle: http://jsfiddle.net/TrueBlueAussie/EzPfK/80/
Notes:
Uses $() to create an empty jQuery object (allows it to be used immediately).
As this one uses the jQuery :input selector it works with select & button elements as well as inputs.
It does not need a DOM ready handler as document is always present.
As the previously focused control is required "elsehere" is is simply stored on window for global use, so it does not need an IIFE function wrapper.

Well depending on what else your page is doing, it could be tricky, but for starters you could have a "blur" event handler attached to the <body> element that just stashes the event target.

To me this seems a slight improvement on Gone Coding's answer:
window.currFocus = document;
// Catch focusin
$(window).on( 'focusin', function () {
window.prevFocus = window.currFocus;
console.log( '£ prevFocus set to:');
console.log( window.currFocus );
window.currFocus = document.activeElement;
});
... there's no stipulation in the question that we're talking exclusively about INPUTs here: it says "previous elements". The above code would also include recording focus of things like BUTTONs, or anything capable of getting focus.

document.getElementById('message-text-area').addEventListener('focus',
event => console.log('FOCUS!')
);
event.relatedTarget has all the data about the previously focused element.
See also https://developer.mozilla.org/en-US/docs/Web/API/Event/Comparison_of_Event_Targets

Here is a slightly different approach which watches both focusin and focusout, in this case to prevent focus to a class of inputs:
<input type="text" name="xyz" value="abc" readonly class="nofocus">
<script>
$(function() {
var leaving = $(document);
$(document).on('focusout', function(e) {
leaving = e.target;
});
$( '.nofocus' ).on('focusin', function(e) {
leaving.focus();
});
$( '.nofocus' ).attr('tabIndex', -1);
});
</script>
Setting tabIndex prevents keyboard users from "getting stuck".

Related

Why is my delegated event handler not working?

I have a button with id of Strike but the JQUERY event doesn't seem to work? This event here worked previously, but ONLY if the button existed when the DOM loaded.
dom.el('Strike').onclick = strikeSkill;
Now I have my buttons dynamically generated, so the "Strike" button is generated later. So the previous code above no longer works, because Strike = Null.
I am using the Jquery .on function, filled up my arguments, but now when I click the strike button, nothing happens. No attacking. Why is this?
function strikeSkill() {
activeCheck();
if (upgradeActive == true){
radialSelector(strike);
}
if (upgradeActive == false){
HitCalc(player.cc, monster.cc);
actor.expCounter(player.cc);
actor.balanceCounter(player.cc, monster.cc);
}
};
$('#Strike').on('click', '#Strike', function() {
strikeSkill();
});
Your current event handler is looking for a #Strike element within #Strike, which is incorrect (not to mention would be invalid HTML).
You can fix this by using a static parent element for the primary selector:
$(document).on('click', '#Strike', function(){
strikeSkill();
});
In the example I used the document, however for best performance it should be the nearest static parent element to #Strike which is available when the DOM loads.

Jquery on mousedown not working on dynamically generated elements

So i'm trying to create a js/css "wave game" like tower defense ones.
When all the pre-generated enemys from first wave are dead, it spawns the second wave and so on.
So far so good.
The problem is that i just can't attack mobs dynamically spawned within second wave.
I used to try .live() in similar cases, but its deprecated, so i'm trying .on(), as instructed
$('.enemy').on('mousedown' , function(event) {
//attack code
}
Its working fine for initial mobs (1st wave) but it still just not working on dynamic mobs (>= 2nd wave)
Help, guys, please?
You need to specify an element that is already there when the DOM is created. In the parameters, you specify the elements you want to add the mousedown method. By simply assigning $('.enemy'), it will attach the method to those that are already present in the DOM.
$('body').on('mousedown', '.enemy', function(event) {
//attack code
}
As Wex mentioned in the comments, instead of writting $('body') you should use the container's name (the container which wraps the .enemy elements. This way, when a .enemy element is added, the event doesn't need to bubble all the way up to the body tag.
The binding '.on()' works only with the content that created earlier then the script ran.
So one solution could be you bind the event to the parent element.
$('.PARENT_ELEMENT').on('mousedown', '.enemy', function(event){
// your code here
}
That should do it.
I made this google like drop down suggestions search box and I faced a problem similar to yours where there was suggestions disappearing before the re-direct happened. I overcame it by using and modifing vyx.ca answer:
var mousedownHappened = false;
var clicked_link;
$("#search-box").blur(function(e) {
if (mousedownHappened)// cancel the blur event
{
mousedownHappened = false;
window.location.href = clicked_link;
} else {
// no link was clicked just remove the suggestions box if exists
if ($('#search-btn').next().hasClass('suggestions')) {
$(".suggestions").remove();
}
}
});
//attaching the event to the document is better
$(document).on('mousedown', '.suggestions a', function() {
clicked_link= $(this).attr('href');
mousedownHappened = true;
});

jQuery: Get reference to click event and trigger it later?

I want to wrap an existing click event in some extra code.
Basically I have a multi part form in an accordion and I want to trigger validation on the accordion header click. The accordion code is used elsewhere and I don't want to change it.
Here's what I've tried:
//Take the click events off the accordion elements and wrap them to trigger validation
$('.accordion h1').each(function (index, value) {
var currentAccordion = $(value);
//Get reference to original click
var originalClick = currentAccordion.click;
//unbind original click
currentAccordion.unbind('click');
//bind new event
currentAccordion.click(function () {
//Trigger validation
if ($('#aspnetForm').valid()) {
current = parseInt($(this).next().find('.calculate-step').attr('data-step'));
//Call original click.
originalClick();
}
});
});
jQuery throws an error because it's trying to do this.trigger inside the originalClick function and I don't think this is what jQuery expects it to be.
EDIT: Updated code. This works but it is a bit ugly!
//Take the click events off the accordion elements and wrap them to trigger validation
$('.accordion h1').each(function (index, value) {
var currentAccordion = $(value);
var originalClick = currentAccordion.data("events")['click'][0].handler;
currentAccordion.unbind('click');
currentAccordion.click(function (e) {
if ($('#aspnetForm').valid()) {
current = parseInt($(this).next().find('.calculate-step').attr('data-step'));
$.proxy(originalClick, currentAccordion)(e);
}
});
});
I think this:
var originalClick = currentAccordion.click;
Isn't actually doing what you think it is - you're capturing a reference to the jQuery click function, rather than event handler you added, so when you call originalClick() it's equivalent to: $(value).click()
I finally came up with something reliable:
$(".remove").each(function(){
// get all our click events and store them
var x = $._data($(this)[0], "events");
var y = {}
for(i in x.click)
{
if(x.click[i].handler)
{
y[i] = x.click[i].handler;
}
}
// stop our click event from running
$(this).off("click")
// re-add our click event with a confirmation
$(this).click(function(){
if(confirm("Are you sure?"))
{
// if they click yes, run click events!
for(i in y)
{
y[i]()
}
return true;
}
// if they click cancel, return false
return false;
})
})
This may seem a bit weird (why do we store the click events in the variable "y"?)
Originally I tried to run the handlers in x.click, but they seem to be destroyed when we call .off("click"). Creating a copy of the handlers in a separate variable "y" worked. Sorry I don't have an in depth explanation, but I believe the .off("click") method removes the click event from our document, along with the handlers.
http://www.frankforte.ca/blog/32/unbind-a-click-event-store-it-and-re-add-the-event-later-with-jquery/
I'm not a jQuery user, but in Javascript, you can set the context of the this keyword.
In jQuery, you use the $.proxy() method to do this.
$.proxy(originalClick, value);
originalClick();
Personally, I'd look at creating callback hooks in your Accordion, or making use of existing callbacks (if they exist) that trigger when opening or closing an accordion pane.
Hope that helps :)
currentAccordion.click is a jQuery function, not the actual event.
Starting with a brute-force approach, what you'd need to do is:
Save references to all the currently bound handlers
Unbind them
Add your own handler, and fire the saved ones when needed
Make sure new handlers bound to click are catched too
This looks like a job for an event filter plugin, but I couldn't find one. If the last point is not required in your application, then it's a bit simpler.
Edit: After some research, the bindIf function shown here looks to be what you'd need (or at least give a general direction)

How to determine where focus went?

This has been asked here before, but several years ago, and there was no cross-platform solution at the time (other than the setTimeout solution, which is really not very handy).
I'd like to do onblur="foo(parm);" and have foo be able to determine which element now has focus.
I'm using regular javascript; no jQuery for this one, please.
Is that possible these days?
You can try something like this:
function whereDidYouGo() {
var all = document.getElementsByTagName('*');
for (var i = 0; i < all.length; i++)
if (all[i] === all[i].ownerDocument.activeElement)
return all[i];
}
EDIT:
function whereDidYouGo() { return document.activeElement; }
In jQuery, at the OP's request:
$(':input').blur(function() {
$focusedElement = $(':input:focus');
//Do stuff with $focusedElement
}
Interesting question. The heart of the matter is - when does the 'focus' event fire, before or after the blur event? If it fires before the blur event, the problem is easy, because you can just store the current focus in a variable that your blur event can access.
However, at least in Chrome 13, it appears the blur event happens before the focus event. One possible solution.
Given the following HTML:
<input id="foo" value='foo' />
<input id="bar" value='bar' />
You can then:
var currentFocus;
var pendingBlur;
var foo = document.getElementById('foo');
foo.addEventListener('focus', function(){
currentFocus = foo;
if(pendingBlur !== undefined){
pendingBlur();
pendingBlur = undefined;
}
});
foo.addEventListener('blur', function(){
pendingBlur = function(){
console.log('new focus:', currentFocus);
};
});
var bar= document.getElementById('bar');
bar.addEventListener('focus', function(){
currentFocus = bar;
if(pendingBlur !== undefined){
pendingBlur();
pendingBlur = undefined;
}
});
bar.addEventListener('blur', function(){
pendingBlur = function(){
console.log('new focus:', currentFocus);
};
});
Basically, I just not the blur callback so it is handy for the focus event to call after we know about which element was focused.
Here is a working example on JSFiddle.
EDIT: This solution suffers from the problem that if you blur on the form by clicking on something other than another form element, the blur event never fires (since we wait for the focus event). The only way around that, that I can conceive, is using a timer to check if pendingBlur is defined, and if so, call it. At which point you don't really need the focus event to call the blur callback anymore...
Year 2020: All major browsers (desktop and mobile) support FocusEvent.relatedTarget.
event.relatedTarget couldn't find the newly-focused element(was div type in my case), but only null gets returned.
After attaching the attribute tabindex="0" on the element, now it works.
<div id="myDiv">myDiv</div>
console.log(e.relatedTarget) // null
<div id="myDiv" tabindex="0"> tabindexed div </div>
console.log(e.relatedTarget) // <div id="myDiv" tabindex="0">
tabindex is an attribute that make elements focusable using tab key on keyboard. I guess it is mainly for web accessibility.
And I guess you won't need to set tabindex attribute as long as to-be focused elements are already accessible by tab key (such as a, select.. and so on)

Issue with selectors & .html() in jquery?

The function associated with the selector stops working when I replace it's contents using .html(). Since I cannot post my original code I've created an example to show what I mean...
Jquery
$(document).ready(function () {
$("#pg_display span").click(function () {
var pageno = $(this).attr("id");
alert(pageno);
var data = "<span id='page1'>1</span><span id='page2'> 2</span><span id='page3'> 3</span>";
$("#pg_display").html(data);
});
});
HTML
<div id="pg_display">
<span id="page1">1</span>
<span id="page2">2</span>
<span id="page3">3</span>
</div>
Is there any way to fix this??...Thanks
Not sure I understand you completely, but if you're asking why .click() functions aren't working on spans that are added later, you'll need to use .live(),
$("#someSelector span").live("click", function(){
# do stuff to spans currently existing
# and those that will exist in the future
});
This will add functionality to any element currently on the page, and any element that is later created. It keeps you have having to re-attach handlers when new elements are created.
You have to re-bind the event after you replace the HTML, because the original DOM element will have disappeared. To allow this, you have to create a named function instead of an anonymous function:
function pgClick() {
var pageno = $(this).attr("id");
alert(pageno);
var data="<span id='page1'>1</span><span id='page2'> 2</span><span id='page3'> 3</span>";
$("#pg_display").html(data);
$("#pg_display span").click(pgClick);
}
$(document).ready(function(){
$("#pg_display span").click(pgClick);
});
That's to be expected, since the DOM elements that had your click handler attached have been replaced with new ones.
The easiest remedy is to use 1.3's new "live" events.
In your situation, you can use 'Event delegation' concept and get it to work.
Event delegation uses the fact that an event generated on a element will keep bubbling up to its parent unless there are no more parents. So instead of binding click event to span, you will find the click event on your #pg_display div.
$(document).ready(
function()
{
$("#pg_display").click(
function(ev)
{
//As we are binding click event to the DIV, we need to find out the
//'target' which was clicked.
var target = $(ev.target);
//If it's not span, don't do anything.
if(!target.is('span'))
return;
alert('page #' + ev.target.id);
var data="<span id='page1'>1</span><span id='page2'>2</span><span id='page3'>3</span>";
$("#pg_display").html(data);
}
);
}
);
Working demo: http://jsbin.com/imuye
Code: http://jsbin.com/imuye/edit
The above code has additional advantage that instead of binding 3 event handlers, it only binds one.
Use the $("#pg_display span").live('click', function....) method instead of .click. Live (available in JQuery 1.3.2) will bind to existing and FUTURE matches whereas the click (as well as .bind) function is only being bound to existing objects and not any new ones. You'll also need (maybe?) to separate the data from the function or you will always add new span tags on each click.
http://docs.jquery.com/Events/live#typefn

Categories

Resources