11

I'm looking to determine which element had the last focus in a series of inputs, that are added dynamically by the user. This code can only get the inputs that are available on page load:

$('input.item').focus(function(){
    $(this).siblings('ul').slideDown();
});

And this code sees all elements that have ever had focus:

$('input.item').live('focus', function(){
    $(this).siblings('ul').slideDown();
});

The HTML structure is this:

<ul>
    <li><input class="item" name="goals[]">
    <ul>
        <li>long list here</li>
        <li>long list here</li>
        <li>long list here</li>
    </ul></li>
</ul>
<a href="#" id="add">Add another</a>

On page load, a single input loads. Then with each add another, a new copy of the top unordered list's contents are made and appended, and the new input gets focus. When each gets focus, I'd like to show the list beneath it. But I don't seem to be able to "watch for the most recently focused element, which exists now or in the future."

To clarify: I'm not looking for the last occurrence of an element in the DOM tree. I'm looking to find the element that currently has focus, even if said element is not present upon original page load.

this image http://droplr.com/174l8H+

So in the above image, if I were to focus on the second element, the list of words should appear under the second element. My focus is currently on the last element, so the words are displayed there.

Do I have some sort of fundamental assumption wrong?

Joshua Cody
  • 3,742
  • 5
  • 32
  • 34
  • What does this sentence mean? *"And this code sees all elements that have ever had focus:"* Sounds to me like you simply want the menu to appear beneath whichever `input` gets focus. Is that right? – user113716 Jun 02 '10 at 01:09
  • That's right, Patrick, but .live performs the function on all inputs that have ever had focus. It sees an element has focus, then stores it. And when you act on focus again, it acts on the currently focused element and the previously focused element, because it was observed by live. See the caveat mentioned by James Kolpack. – Joshua Cody Jun 02 '10 at 01:20
  • What version of jQuery are you using? `live()` works fine for me when hiding and showing siblings. The event on one `input` shouldn't affect the others. Shouldn't matter when they were loaded. – user113716 Jun 02 '10 at 01:31

5 Answers5

5

document.activeElement is what you want. It's part of HTML5 and supported by all modern browsers, including IE.

Eli Grey
  • 35,104
  • 14
  • 75
  • 93
4

According to the documentation (see 'caveats'), .live() in jQuery 1.4.1 supports focus, mapping to focusin. I'd suggest creating an element in common scope to hold the last focused element. Perhaps like so:

var lastFocused;
$('input.item').live('focusin', function(){
    lastFocused = $(this);
});
James Kolpack
  • 9,331
  • 2
  • 44
  • 59
2

How to determine which html page element has focus?

Has your answer (using document.activeElement gets you there for many browsers, but to make the ones that don't support it work you'll want to add the Javascript from that question's answer).

Community
  • 1
  • 1
machineghost
  • 33,529
  • 30
  • 159
  • 234
1

In the end, it was an error in code elsewhere that was confusing the DOM about who had focus.

The line was this: $('#item-add').find('input.item').focus();

And it needed to be this: $('#item-add:last').find('input.item').focus();

Because the added item is always last in the list.

Much has been learned, and I've tried to start and upvote accordingly. Particularly of note to the question at large:

  • .live events are not cumulative. Only code cruft is.
  • Set a variable outside of your function and update it within your function so you can access it in other functions as well.
  • jsfiddle.net and jsbin.com are awesome.
  • Holy HTML5, document.activeElement is good to know.

Thanks so much, SO, for all your help on this issue.

Joshua Cody
  • 3,742
  • 5
  • 32
  • 34
0

You can use the :last selector to only handle the event on the last <input> in the document (last at the time that the event was fired)

$('input.item:last').live('focus', function(){
    $(this).siblings('ul').slideDown();
});
SLaks
  • 868,454
  • 176
  • 1,908
  • 1,964
  • This is close, SLaks, and I'll edit my post to reflect the issue. I don't need it to add to the last matched element in the tree. I need it to add to the element which had focus most recently. This code fails if you add multiple items, then focus on element that appears earlier in the DOM. – Joshua Cody Jun 02 '10 at 00:48
  • That doesn't make any sense. At any time, the element that the event just fired for is the one that had focus most recently. – SLaks Jun 02 '10 at 00:49
  • SLaks, I added an image to display my concern here. The issue is that if I use your code, it constantly watches for the last element in the DOM tree. But then if you go click on an earlier element, it is still working on the last occurrence of `input.item` -- is this making any more sense yet? – Joshua Cody Jun 02 '10 at 00:53
  • Are you asking to hide the other `
      `s?
    – SLaks Jun 02 '10 at 00:55
  • I'm handling that hiding on blur. I'm simply looking to show the `ul` that is a sibling of **whichever** input a user focuses on, but only one at a time. – Joshua Cody Jun 02 '10 at 00:57
  • @Joshua - I'm not understanding either, it seems that the second block of code in your question would do what you're describing... – Nick Craver Jun 02 '10 at 01:22
  • @Nick, perhaps I have something wrong somewhere else. But when I use .live and watch for focus, it performs the action on all elements that have ever received focus, not only the element that just had focus. – Joshua Cody Jun 02 '10 at 01:26
  • 1
    @Joshua - What's different from this example: http://jsfiddle.net/xhFga/ that would figure it out :) – Nick Craver Jun 02 '10 at 01:31
  • @Nick Awesome! This is a huge help. Bookmarked for future keeping, and investigating now. – Joshua Cody Jun 02 '10 at 01:35