Як дізнатися, який елемент DOM має фокус?


1309

Я хотів би дізнатися в JavaScript, який елемент на даний момент фокусується. Я переглядав DOM і ще не знайшов того, що мені потрібно. Чи є спосіб це зробити, і як?

Причина, по якій я шукав це:

Я намагаюсь робити клавіші, як стрілки, і enterпереміщуватися по таблиці вхідних елементів. Вкладка працює зараз, але введіть, і стрілки за замовчуванням не здаються. У мене налаштована частина керування ключами, але тепер мені потрібно розібратися, як перемістити фокус у функціях обробки подій.


2
Ось закладка, що console.log елемент з фокусом: github.com/lingtalfi/where-is-focus-bookmarklet
ling

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

Відповіді:


1533

Використовуйте document.activeElement, він підтримується у всіх основних браузерах.

Раніше, якщо ви намагалися з’ясувати, яке поле форми має фокус, ви не могли. Щоб імітувати виявлення в старих браузерах, додайте обробник подій "фокусування" до всіх полів і запишіть останнє зосереджене поле у ​​змінну. Додайте обробник "розмиття", щоб очистити змінну після події розмиття для останнього сфокусованого поля.

Якщо вам потрібно видалити, activeElementви можете використовувати розмиття; document.activeElement.blur(). Це змінить activeElementна body.

Пов’язані посилання:


53
Не впевнений у IE, але FF і Safari обидва повертають елемент BODY.
JW.

10
activeElementнасправді не повертає сфокусований елемент. Будь-який елемент може мати фокус. Якщо в документі є 4 "прокрутки", 0 або 1 цих символів можна прокручувати клавішами зі стрілками. Якщо ви натиснете на нього, цей розділ буде зосереджено. Якщо натиснути поза всіма, тіло зосереджене. Як дізнатися, на який сфокусований фокус? 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повинен бути завершеним, try catchоскільки за певних обставин він може спричинити виняток (не лише IE9 AFAIK). Дивіться bugs.jquery.com/ticket/13393 та bugs.jqueryui.com/ticket/8443
robocat

127

За словами JW, ви не можете знайти поточний сфокусований елемент, принаймні, незалежно від браузера. Але якщо ваш додаток лише IE (деякі з них ...), ви можете знайти його наступним чином:

document.activeElement

EDIT: Схоже, в IE все не так, все-таки є частиною проекту HTML5 і, мабуть, підтримується найновішою версією Chrome, Safari та Firefox.


5
FF3 теж. Це фактично частина специфікації HTML5 навколо "управління фокусом".
Півмісяць свіжий

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 не вдається повернути те, що ви натиснули (повернення невизначеного та елемента тіла документа відповідно). PS Я не можу повірити, що цій темі вже 2 роки, і все ще є проблеми з регресією у веб-програмі, а також у тому, де пропускні посилання не працюють, але так багато роботи робиться з додаванням експериментального css3. Подумайте, я можу повернутися до того, як рекомендувати Firefox моїй родині та друзям.
Світанок

^^ document.activeElement добре, коли клацнути, щоб зосередити увагу на текстовій області або іншому введенні. Якщо це посилання, воно не фокусуватиметься при натисканні на нього (на сторінці чи очевидно інакше).
marksyzm

84

Якщо ви можете використовувати jQuery, він тепер підтримує: фокус, просто переконайтеся, що ви використовуєте версію 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') нуль.
Манфред

Дивіться мою відповідь , щоб уникнути використання querySelector: stackoverflow.com/a/40873560/2624876
1j01

10

Мені сподобався підхід, застосовуваний Джоелом 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, перевірте у розділі "Селектори", де він вказує на документи W3C CSS . Я тестував 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 на цьому рівні ШЛУХО швидше та ефективніше. У цьому випадку ви можете не помітити, але в цілому за системою ви помітите різницю.
Martijn

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не гарантовано є текстовим вузлом.
1j01

6

Можливі проблеми з використанням document.activeElement. Поміркуйте:

<div contentEditable="true">
  <div>Some text</div>
  <div>Some text</div>
  <div>Some text</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.