2

The document.activeElement attribute:

Returns the currently focused element, that is, the element that will get keystroke events if the user types any

This is clear for a single browsing context (single document + window). If a focusable element (input, textarea contenteditable element) has a visible selection, it will be the document.activeElement element.

When you have one or more iframes on the page and they have focusable elements, the behavior changes.

If an input in an iframe is focused, the parent document’s activeElement property will point to that iframe’s iframe element (the actual <iframe> node). However, any previously selected text in focusable elements will stay visibly selected, though it visually appears inactive (greyed-out):

focused iframe input

If you then focus back to the input in the parent document, the iframe’s input will no longer be the iframe document’s activeElement, and it will also appear visibly selected though inactive:

focused parent input

How can I check for any focusable elements in a document with a visible selection, even when it isn’t the document’s activeElement?


Edit

I’ve made a demo playground to illustrate the behavior: http://codepen.io/acusti/pen/PGakzY?editors=0010

Andrew Patton
  • 5,344
  • 3
  • 30
  • 30

2 Answers2

0

You can store the .activeElement or selection in an array; for example at an event, then select the element or selection at index array.length - 2 of the array.

See also How to clear the contents of an iFrame from another iFrame , How to pass css :active pseudo class to javascript?

Community
  • 1
  • 1
guest271314
  • 1
  • 15
  • 104
  • 177
  • Sigh. I’m hoping there’s another way. Caching the previous activeElement isn’t a reliable way to determine if the selection is still visible. For example, if you are in an iframe and have a selection in an input, then click to a separate iframe's input, the selection will still be visible (but appear inactive). However, if you click from the selected input to the body of the *same* iframe, the selection will no longer be visible. For both, iframe’s document.activeElement will have gone from being the input with selected text to the iframe’s body, with no way to tell the difference. – Andrew Patton Oct 12 '16 at 16:44
  • 1
    @AndrewPatton See http://stackoverflow.com/questions/3169786/clear-text-selection-with-javascript – guest271314 Oct 12 '16 at 17:16
  • 1
    So `window.getSelection` was in fact the missing piece! It will return the “inactive” visible selection in a browsing context. Now I need to figure out how to use `document.selection.createRange` as my fallback. If you’d like to make an answer (or edit your existing one) using window.getSelection, I’d be happy to accept it, or else I’ll make one myself. Thanks! – Andrew Patton Oct 13 '16 at 14:01
  • @AndrewPatton Perhaps posting an Answer to your own Question would be best way to convey what you have found using `window.getSelection` and the approach that you are describing which resolved Question. – guest271314 Oct 13 '16 at 16:34
0

@guest271314 pointed me in the right direction for solving this issue. As illustrated in this demo, using document.getSelection() allows you to find any focusable elements in a browsing context with a visible selection, even when they are “inactive” because a different browsing context (or another browser window or part of the browser) is active. The getSelection() API does require a bit of extra logic to deal with the different kind of things it can return. But for the purposes of identifying a selection with a focusable element (input, textarea), after using document.getSelection() and then checking anchorNode in the returned selection object for childNodes && childNodes.length, you can reliably get the element with the selection by accessing selection.anchorNode.childNodes[selection.anchorOffset].

Andrew Patton
  • 5,344
  • 3
  • 30
  • 30