Как узнать, какой элемент DOM имеет фокус?


1309

Я хотел бы узнать в JavaScript, какой элемент в настоящее время имеет фокус. Я просматривал DOM и пока не нашел то, что мне нужно. Есть ли способ сделать это и как?

Причина, по которой я искал это:

Я пытаюсь сделать клавиши как стрелки и enterперемещаться по таблице элементов ввода. Вкладка теперь работает, но вводи, а стрелки не по умолчанию вроде бы. Я настроил часть обработки ключей, но теперь мне нужно выяснить, как переместить фокус в функции обработки событий.


2
Вот букмарклет, который console.log элемента с фокусом: github.com/lingtalfi/where-is-focus-bookmarklet
Лин

Вы можете использовать find-focused-elementпакет: npmjs.com/package/find-focused-element
Максим Жуков

Ответы:


1533

Используйте document.activeElement, это поддерживается во всех основных браузерах.

Ранее, если вы пытались выяснить, какое поле формы имеет фокус, вы не могли этого сделать. Чтобы эмулировать обнаружение в старых браузерах, добавьте обработчик события «focus» ко всем полям и запишите последнее сфокусированное поле в переменной. Добавьте обработчик «размытия», чтобы очистить переменную после события размытия для последнего сфокусированного поля.

Если вам нужно удалить, activeElementвы можете использовать размытие; document.activeElement.blur(), Это изменит activeElementTo body.

Ссылки по теме:


53
Не уверен насчет IE, но FF и Safari оба возвращают элемент BODY.
JW.

10
activeElementна самом деле не возвращает сфокусированный элемент. Любой элемент может иметь фокус. Если документ имеет 4 scrolldivs, 0 или 1 из этих div можно прокручивать клавишами со стрелками. Если вы нажмете один, этот div будет сфокусирован. Если вы нажмете за пределами всего, тело сфокусировано. Как вы узнаете, какой скроллдив сфокусирован? jsfiddle.net/rudiedirkx/bC5ke/show (проверьте консоль)
Rudie

18
@Rudie, @Stewart: я создал вашу скрипку для создания более сложной игровой площадки: jsfiddle.net/mklement/72rTF . Вы обнаружите, что единственный крупный браузер (по состоянию на конец 2012 года), который на самом деле может сфокусировать такое, - divэто Firefox 17, и только при помощи вкладок . Типы элементов, через document.activeElementкоторые возвращаются ВСЕ основные браузеры , ограничены элементами, связанными с вводом . Если ни один такой элемент не имеет фокуса, все основные браузеры возвращают bodyэлемент - кроме IE 9, который возвращает htmlэлемент.
mklement0

10
Не уверен, поможет ли это, но вы можете сделать такой элемент, как div, получить фокус клавиатуры, включив атрибут tabindex = "0"
Marco Luglio

4
Любой доступ document.activeElementдолжен быть заключен в a, так try catchкак при некоторых обстоятельствах он может вызвать исключение (не только IE9 AFAIK). См. Bugs.jquery.com/ticket/13393 и bugs.jqueryui.com/ticket/8443
robocat

127

Как сказал JW, вы не можете найти текущий фокусированный элемент, по крайней мере, независимо от браузера. Но если ваше приложение предназначено только для IE (некоторые из них ...), вы можете найти его следующим образом:

document.activeElement

РЕДАКТИРОВАТЬ: Похоже, что IE не все не так, в конце концов, это часть проекта HTML5 и, кажется, поддерживается по крайней мере последней версии Chrome, Safari и Firefox.


5
FF3 тоже. На самом деле это часть спецификации HTML5 вокруг «управления фокусом».
Crescent Fresh

2
Работает в текущей версии Chrome и Opera (9.62). Не работает в Safari 3.2.3 на OS X, но работает в Safari 4, выпущенном вчера :)
gregers

то же самое для хрома 19: S
Себас

1
Он работает только в chrome (20) / safari (5.1.3), когда вы используете клавиатуру для перехода на элемент. Если вы щелкнете по нему, то ни селектор jquery: focus, ни document.activeElement не вернут то, что вы щелкнули (возвращая undefined и элемент body документа соответственно). PS Я не могу поверить, что этой теме уже 2 года, и в webkit все еще есть проблемы регрессии, наряду с той, где пропускаемые ссылки не работают, но так много работы делается с добавлением экспериментального css3. Думаю, я могу вернуться к рекомендации Firefox моей семье и друзьям.
Рассвет

^^ document.activeElement - это хорошо, когда вы нажимаете, чтобы сфокусироваться на текстовой области или другом вводе. Если это ссылка, она не будет фокусироваться при нажатии (на странице или, очевидно, в противном случае).
markyzm

84

Если вы можете использовать jQuery, он теперь поддерживает: focus, просто убедитесь, что вы используете версию 1.6+.

Это утверждение даст вам в настоящее время сосредоточенный элемент.

$(":focus")

От: Как выбрать элемент, который фокусируется на нем, с помощью jQuery


4
Это хорошо, но как это делает jQuery? document.activeElement? Я нашел это: return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
Гарри Пехконен

46

document.activeElementв настоящее время является частью спецификации рабочего проекта HTML5 , но она может еще не поддерживаться в некоторых не основных / мобильных / старых браузерах. Вы можете вернуться к querySelector(если это поддерживается). Также стоит упомянуть, что document.activeElementон вернется, document.bodyесли ни один элемент не сфокусирован, даже если окно браузера не имеет фокуса.

Следующий код поможет обойти эту проблему и querySelectorпредоставит немного лучшую поддержку.

var focused = document.activeElement;
if (!focused || focused == document.body)
    focused = null;
else if (document.querySelector)
    focused = document.querySelector(":focus");

Кроме того, следует отметить разницу в производительности между этими двумя методами. Запрос к документу с помощью селекторов всегда будет намного медленнее, чем доступ к activeElementсвойству. Смотрите этот тест jsperf.com .


21

Сам по себе document.activeElementможет возвращать элемент, если документ не сфокусирован (и, следовательно, ничто в документе не сфокусировано!)

Вы можете хотеть такого поведения, или оно может не иметь значения (например, в рамках keydownсобытия), но если вам нужно знать, что что-то действительно сфокусировано, вы можете дополнительно проверить document.hasFocus().

Следующее даст вам сфокусированный элемент, если он есть, или еще null.

var focused_element = null;
if (
    document.hasFocus() &&
    document.activeElement !== document.body &&
    document.activeElement !== document.documentElement
) {
    focused_element = document.activeElement;
}

Чтобы проверить, имеет ли конкретный элемент фокус, проще:

var input_focused = document.activeElement === input && document.hasFocus();

Чтобы проверить, сфокусировано ли что-нибудь , еще раз сложнее:

var anything_is_focused = (
    document.hasFocus() &&
    document.activeElement !== null &&
    document.activeElement !== document.body &&
    document.activeElement !== document.documentElement
);

Примечание о надежности : в коде, в котором выполняется проверка, document.bodyи document.documentElementэто происходит потому, что некоторые браузеры возвращают один из них или nullкогда ничего не сфокусировано.

Он не учитывает, имел ли <body>(или может быть <html>) tabIndexатрибут и, таким образом, мог бы быть сфокусирован . Если вы пишете библиотеку или что-то еще и хотите, чтобы она была надежной, вам, вероятно, следует как-то с этим справиться.


Вот ( тяжелые воздушные кавычки) «однострочная» версия получения сфокусированного элемента, концептуально более сложная, потому что вы должны знать о коротком замыкании, и вы знаете, что он явно не помещается в одну строку, если вы хочу, чтобы это было читабельно.
Я не буду рекомендовать это. Но если вы 1337 hax0r, idk ... это там.
Вы также можете удалить || nullчасть, если вы не против получить falseв некоторых случаях. (Вы все еще можете получить, nullесли document.activeElementесть null):

var focused_element = (
    document.hasFocus() &&
    document.activeElement !== document.body &&
    document.activeElement !== document.documentElement &&
    document.activeElement
) || null;

Для проверки того, что конкретный элемент сфокусирован, вы также можете использовать события, но этот способ требует настройки (и, возможно, демонтажа), и, что важно, предполагает исходное состояние :

var input_focused = false;
input.addEventListener("focus", function() {
    input_focused = true;
});
input.addEventListener("blur", function() {
    input_focused = false;
});

Вы могли бы исправить предположение о начальном состоянии, используя нечетный способ, но тогда вы могли бы просто использовать его вместо этого.


15

document.activeElementможет по умолчанию к <body>элементу, если фокусируемые элементы не находятся в фокусе. Кроме того, если элемент сфокусирован, а окно браузера размыто, activeElementбудет продолжать удерживать сфокусированный элемент.

Если какой- либо из этих двух видов поведения не является желательным, рассмотрит подход CSS на основе: document.querySelector( ':focus' ).


Круто, да, в моем случае ваш подход имел абсолютный смысл. Я могу установить свои фокусируемые элементы с помощью 'tabindex = "- 1"', если ни один из них не имеет фокуса (скажем, некоторого текста или рисунка, который меня не волнует), то document.querySelector (': focus') возвращает значение NULL.
Манфред

Смотрите мой ответ, чтобы избежать использования querySelector: stackoverflow.com/a/40873560/2624876
1j01

10

Мне понравился подход, используемый Joel S, но я также люблю простоту document.activeElement. Я использовал jQuery и объединил их. Старые браузеры, которые не поддерживают, document.activeElementбудут использовать jQuery.data()для хранения значения hasFocus. Новые браузеры будут использовать document.activeElement. Я предполагаю, что document.activeElementбудет иметь лучшую производительность.

(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);

3
Может ли это быть заменено @William Denniss's $("*:focus")?
Pylinux

Я полагаю, что это возможно. Я написал это давным-давно, и у меня никогда не было оснований пересматривать лучшее решение сейчас, когда оно прошло 5 лет. Попробуйте! Я мог бы просто сделать то же самое. Я меньше плагин на нашем сайте! :)
Джейсон

10

Маленький помощник, который я использовал для этих целей в 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);

Таким образом, вы можете проверить, имеет ли элемент фокус при el.hasFocus()условии, что startFocusTracking()он был вызван для данного элемента.


7

JQuery поддерживает :focusпсевдокласс на текущий момент. Если вы ищете его в документации JQuery, отметьте в разделе «Селекторы», где он указывает на документацию CSS W3C . Я тестировал с Chrome, FF и IE 7+. Обратите внимание, что для того, чтобы он работал в IE, <!DOCTYPE...должен существовать на странице HTML. Вот пример, предполагающий, что вы присвоили идентификатор элементу, который имеет фокус:

$(":focus").each(function() {
  alert($(this).attr("id") + " has focus!");
});

1
Вы должны (всегда?) Использовать this.idвместо $(this).attr('id')или, по крайней мере (когда у вас уже есть объект jQuery) $(this)[0].id. Родной Javascript на этом уровне гораздо быстрее и эффективнее. В этом случае может не быть заметным, но в масштабе всей системы вы заметите разницу.
Мартейн

7

Если вы хотите получить объект, который является экземпляром Element, вы должны использовать document.activeElement, но если вы хотите получить объект, который является экземпляром Text, вы должны использовать document.getSelection().focusNode.

Надеюсь поможет.


Лучше чем?
1j01

Откройте инспектор вашего браузера, нажмите в любом месте страницы, пройдите мимо document.getSelection().focusNode.parentElementи нажмите Enter. После этого document.activeElementпрошёл и сделал то же самое. ;)
rplaurindo

С этим сосредоточенным комментарием, document.activeElementдает <textarea>то, что document.getSelection().focusNodeдает то, <td>что содержит <textarea>document.getSelection().focusNode.parentElementдает <tr>содержащее <td>)
1j01

Извините, мои извинения. Я не объяснил это хорошо. Если вы хотите получить объект, который является экземпляром Element, вы должны использовать document.activeElement, но если вы хотите получить объект, который является экземпляром Text, вы должны использовать document.getSelection().focusNode. Пожалуйста, проверьте это снова. Я надеюсь, что помог.
rplaurindo

2
Вопрос в том, какой элемент в настоящее время находится в фокусе. И focusNodeне обязательно будет текстовым узлом.
января 01

6

Есть потенциальные проблемы с использованием document.activeElement. Рассматривать:

<div contentEditable="true">
  <div>Some text</div>
  <div>Some text</div>
  <div>Some text</div>
</div>

Если пользователь фокусируется на внутреннем div, то document.activeElement все еще ссылается на внешний div. Вы не можете использовать document.activeElement, чтобы определить, какой из внутренних элементов имеет фокус.

Следующая функция обходит это и возвращает сфокусированный узел:

function active_node(){
  return window.getSelection().anchorNode;
}

Если вы предпочитаете получить сфокусированный элемент, используйте:

function active_element(){
  var anchor = window.getSelection().anchorNode;
  if(anchor.nodeType == 3){
        return anchor.parentNode;
  }else if(anchor.nodeType == 1){
        return anchor;
  }
}

3
На самом деле это не проблема document.activeElement: внутренние <div>элементы на самом деле не могут получить фокус, как вы можете видеть визуально, установив :focusпсевдокласс на что-то видимое (пример: jsfiddle.net/4gasa1t2/1 ). То, о чем вы говорите, это то, что из внутренних элементов <div>содержит выделение или символ каретки, что является отдельной проблемой.
Тим Даун

6

Я обнаружил, что следующий фрагмент полезен при попытке определить, какой элемент в данный момент имеет фокус. Скопируйте следующее в консоль вашего браузера, и каждую секунду он будет распечатывать детали текущего элемента, который имеет фокус.

setInterval(function() { console.log(document.querySelector(":focus")); }, 1000);

Не стесняйтесь изменять, console.logчтобы выйти из системы, чтобы помочь вам точно определить точный элемент, если распечатка всего элемента не поможет вам точно определить элемент.


5

Читая другие ответы и пробуя себя, кажется document.activeElement, вы получите элемент, который вам нужен в большинстве браузеров.

Если у вас есть браузер, который не поддерживает document.activeElement, если у вас есть jQuery, вы сможете заполнить его на всех событиях фокуса чем-то очень простым, как это (не проверено, поскольку у меня нет браузера, соответствующего этим критериям) ):

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
  });
}

5

Если вы используете jQuery, вы можете использовать это, чтобы узнать, активен ли элемент:

$("input#id").is(":active");


3

Просто поместите это здесь, чтобы дать решение, которое я в конечном итоге придумала.

Я создал свойство под названием document.activeInputArea и использовал дополнение HotKeys для jQuery, чтобы перехватывать события клавиатуры для клавиш со стрелками, табуляции и ввода, и я создал обработчик событий для нажатия на элементы ввода.

Затем я настраивал activeInputArea при каждом изменении фокуса, чтобы я мог использовать это свойство, чтобы узнать, где я был.

Хотя это легко испортить, потому что если у вас есть ошибка в системе, а фокус не там, где вы думаете, то восстановить правильную фокусировку очень сложно.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.