Bagaimana cara mengetahui elemen DOM mana yang memiliki fokus?

1309

Saya ingin mencari tahu, dalam JavaScript, elemen mana yang saat ini memiliki fokus. Saya telah mencari melalui DOM dan belum menemukan apa yang saya butuhkan. Apakah ada cara untuk melakukan ini, dan bagaimana?

Alasan saya mencari ini:

Saya mencoba membuat kunci seperti panah dan entermenavigasi melalui tabel elemen input. Tab berfungsi sekarang, tetapi masuk, dan panah tampaknya tidak secara default. Saya sudah menyiapkan bagian penanganan kunci tetapi sekarang saya harus mencari cara untuk memindahkan fokus pada fungsi penanganan acara.

Tony Peterson
sumber
2
Berikut adalah bookmarklet yang console.log elemen dengan fokus: github.com/lingtalfi/where-is-focus-bookmarklet
ling
Anda dapat menggunakan find-focused-elementpaket: npmjs.com/package/find-focused-element
Maxim Zhukov

Jawaban:

1533

Gunakan document.activeElement, itu didukung di semua browser utama.

Sebelumnya, jika Anda mencoba mencari tahu bidang formulir apa yang memiliki fokus, Anda tidak bisa. Untuk meniru deteksi dalam browser lama, tambahkan pengendali event "fokus" ke semua bidang dan rekam bidang yang terakhir difokuskan dalam suatu variabel. Tambahkan handler "blur" untuk menghapus variabel pada peristiwa blur untuk bidang yang terakhir difokuskan.

Jika Anda perlu menghapus activeElementAnda dapat menggunakan blur; document.activeElement.blur(). Ini akan mengubah activeElementke body.

Tautan yang berhubungan:

Dave Jarvis
sumber
53
Tidak yakin tentang IE, tetapi FF dan Safari sama-sama mengembalikan elemen BODY.
JW.
10
activeElementsebenarnya tidak mengembalikan elemen fokus. Setiap elemen dapat memiliki fokus. Jika suatu dokumen memiliki 4 'scrolldivs', 0 atau 1 dari divs tersebut dapat digulir dengan tombol panah. Jika Anda mengklik satu, div itu fokus. Jika Anda mengklik di luar semua, tubuh fokus. Bagaimana Anda mengetahui scrolldiv mana yang difokuskan? jsfiddle.net/rudiedirkx/bC5ke/show (periksa konsol)
Rudie
18
@Rudie, @Stewart: Saya telah membangun biola Anda untuk membuat taman bermain yang lebih rumit: jsfiddle.net/mklement/72rTF . Anda akan menemukan bahwa satu-satunya browser utama (pada akhir 2012) yang benar-benar dapat fokus divadalah Firefox 17, dan hanya dengan menabraknya . Jenis elemen yang dikembalikan SEMUA browser utama document.activeElementdibatasi pada elemen yang terkait dengan input . Jika tidak ada elemen yang memiliki fokus, semua browser utama mengembalikan bodyelemen - kecuali IE 9, yang mengembalikan htmlelemen.
mklement0
10
Tidak yakin apakah itu membantu, tetapi Anda dapat membuat elemen seperti div menerima fokus keyboard dengan memasukkan atribut tabindex = "0"
Marco Luglio
4
Akses apa pun ke document.activeElementharus dibungkus dalam try catchseperti dalam beberapa keadaan dapat menyebabkan pengecualian (tidak hanya IE9 AFAIK). Lihat bugs.jquery.com/ticket/13393 dan bugs.jqueryui.com/ticket/8443
robocat
127

Seperti yang dikatakan oleh JW, Anda tidak dapat menemukan elemen fokus saat ini, setidaknya dengan cara yang bebas browser. Tetapi jika aplikasi Anda hanya untuk IE (ada beberapa ...), Anda dapat menemukannya dengan cara berikut:

document.activeElement

EDIT: Sepertinya IE tidak memiliki semuanya salah, ini adalah bagian dari konsep HTML5 dan tampaknya didukung oleh versi terbaru dari Chrome, Safari dan Firefox.

Wookai
sumber
5
FF3 juga. Ini sebenarnya bagian dari spesifikasi HTML5 seputar "manajemen fokus".
Crescent Fresh
2
Ini berfungsi di versi Chrome dan Opera saat ini (9.62). Tidak bekerja di Safari 3.2.3 pada OS X, tetapi bekerja di Safari 4 yang dirilis kemarin :)
Gregers
masih sama untuk chrome 19: S
Sebas
1
Ini hanya bekerja di chrome (20) / safari (5.1.3) ketika Anda menggunakan keyboard untuk tab ke elemen. Jika Anda mengkliknya maka jquery: selector fokus atau document.activeElement tidak berhasil mengembalikan apa yang Anda klik (masing-masing mengembalikan elemen yang tidak terdefinisi dan dokumen). PS Saya tidak percaya utas ini berumur 2 tahun dan masih ada masalah regresi di webkit, bersama dengan yang mana lompatan tautannya tidak berfungsi, tetapi begitu banyak pekerjaan yang dilakukan dengan menambahkan css3 eksperimental. Rasanya saya bisa kembali merekomendasikan firefox kepada keluarga dan teman-teman saya.
Dawn
^^ document.activeElement baik-baik saja ketika Anda mengklik untuk fokus pada area teks atau input lainnya. Jika itu adalah tautan, tautan itu tidak akan fokus ketika diklik (di halaman atau jelas sebaliknya).
markyzm
84

Jika Anda dapat menggunakan jQuery, sekarang mendukung: fokus, pastikan Anda menggunakan versi 1.6+.

Pernyataan ini akan memberi Anda elemen yang sedang difokuskan.

$(":focus")

Dari: Cara memilih elemen yang memiliki fokus dengan jQuery

William Denniss
sumber
4
Ini bagus, tetapi bagaimana jQuery melakukannya? document.activeElement? Saya menemukan ini: return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
Harry Pehkonen
46

document.activeElementsekarang merupakan bagian dari spesifikasi rancangan HTML5 , tetapi mungkin belum didukung di beberapa peramban non-utama / seluler / yang lebih lama. Anda dapat kembali ke querySelector(jika itu didukung). Perlu juga disebutkan bahwa document.activeElementakan kembali document.bodyjika tidak ada elemen yang difokuskan - bahkan jika jendela browser tidak memiliki fokus.

Kode berikut akan mengatasi masalah ini dan kembali querySelectormemberikan sedikit dukungan yang lebih baik.

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

Hal tambahan yang perlu diperhatikan adalah perbedaan kinerja antara kedua metode ini. Meminta dokumen dengan pemilih akan selalu jauh lebih lambat daripada mengakses activeElementproperti. Lihat tes jsperf.com ini .

Andy E
sumber
21

Dengan sendirinya, document.activeElementmasih dapat mengembalikan elemen jika dokumen tidak fokus (dan dengan demikian tidak ada dalam dokumen yang difokuskan!)

Anda mungkin menginginkan perilaku itu, atau mungkin tidak masalah (misalnya dalam suatu keydownacara), tetapi jika Anda perlu tahu ada sesuatu yang benar-benar fokus, Anda juga dapat memeriksanya document.hasFocus().

Berikut ini akan memberi Anda elemen fokus jika ada, atau yang lain null.

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

Untuk memeriksa apakah elemen tertentu memiliki fokus, lebih sederhana:

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

Untuk memeriksa apakah ada yang fokus, lebih kompleks lagi:

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

Catatan Robustness : Dalam kode di mana ia mengecek document.bodydan document.documentElement, ini karena beberapa browser mengembalikan salah satunya atau nullketika tidak ada yang difokuskan.

Itu tidak menjelaskan apakah <body>(atau mungkin <html>) memiliki tabIndexatribut dan dengan demikian bisa benar-benar fokus . Jika Anda sedang menulis perpustakaan atau sesuatu dan ingin menjadi kuat, Anda mungkin harus menanganinya.


Ini versi (satu airquotes berat ) "satu-liner" untuk mendapatkan elemen fokus, yang secara konsep lebih rumit karena Anda harus tahu tentang hubungan arus pendek, dan Anda tahu, itu jelas tidak cocok pada satu baris, dengan asumsi Anda ingin itu dapat dibaca.
Saya tidak akan merekomendasikan yang ini. Tetapi jika Anda 1337 hax0r, idk ... itu ada di sana.
Anda juga dapat menghapus || nullbagian jika Anda tidak keberatan falsedalam beberapa kasus. (Kamu masih bisa mendapatkannyanull jika document.activeElementadalah null):

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

Untuk memeriksa apakah elemen tertentu difokuskan, Anda juga bisa menggunakan acara, tetapi cara ini memerlukan pengaturan (dan berpotensi runtuh), dan yang penting, mengasumsikan keadaan awal :

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

Anda bisa memperbaiki asumsi keadaan awal dengan menggunakan cara yang tidak terjadi, tetapi kemudian Anda bisa saja menggunakannya.

1j01
sumber
15

document.activeElementmungkin default ke <body>elemen jika tidak ada elemen yang bisa fokus dalam fokus. Selain itu, jika elemen difokuskan dan jendela browser buram,activeElement akan terus menahan elemen fokus.

Jika salah satu dari dua perilaku ini tidak diinginkan, mempertimbangkan pendekatan berbasis CSS: document.querySelector( ':focus' ).

Nate Whittaker
sumber
Keren, ya, dalam kasus saya, pendekatan Anda benar-benar masuk akal. Saya dapat mengatur elemen fokus saya dengan 'tabindex = "- 1"', jika tidak ada yang memiliki fokus (katakanlah, beberapa teks atau gambar, yang saya tidak pedulikan) document.querySelector (': fokus') mengembalikan batal.
Manfred
Lihat jawaban saya untuk menghindari penggunaan querySelector: stackoverflow.com/a/40873560/2624876
1j01
10

Saya menyukai pendekatan yang digunakan oleh Joel S, tetapi saya juga suka kesederhanaannya document.activeElement. Saya menggunakan jQuery dan menggabungkan keduanya. Browser lama yang tidak mendukung document.activeElementakan digunakan jQuery.data()untuk menyimpan nilai 'hasFocus'. Browser yang lebih baru akan digunakan document.activeElement. Saya berasumsi bahwa document.activeElementakan memiliki kinerja yang lebih baik.

(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);
Jason
sumber
3
Bisakah ini digantikan oleh @William Denniss's $("*:focus")?
Pylinux
Saya kira itu bisa. Saya menulis ini sejak lama dan tidak pernah punya alasan untuk meninjau kembali solusi yang lebih baik sekarang karena 5 tahun kemudian. Cobalah! Saya mungkin akan melakukan hal yang sama. Saya kurang plugin di situs kami! :)
Jason
10

Seorang pembantu kecil yang saya gunakan untuk keperluan ini di 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);

Dengan cara ini Anda dapat memeriksa apakah elemen memiliki fokus el.hasFocus()asalkan startFocusTracking()telah dipanggil pada elemen yang diberikan.

Joel S
sumber
7

JQuery mendukung :focuspseudo-class saat ini. Jika Anda mencarinya di dokumentasi JQuery, centang di bawah "Selectors" di mana ia mengarahkan Anda ke dokumen CSS W3C . Saya sudah menguji dengan Chrome, FF, dan IE 7+. Perhatikan bahwa agar berfungsi di IE, <!DOCTYPE...harus ada di halaman html. Berikut adalah contoh dengan asumsi Anda telah menetapkan id ke elemen yang memiliki fokus:

$(":focus").each(function() {
  alert($(this).attr("id") + " has focus!");
});
DeezCashews
sumber
1
Anda harus (selalu?) Menggunakan this.idalih-alih $(this).attr('id'), atau setidaknya (ketika Anda sudah memiliki objek jQuery Anda) $(this)[0].id. Javascript asli pada level ini adalah WAY lebih cepat dan lebih efisien. Mungkin tidak terlihat dalam kasus ini, tetapi di seluruh sistem Anda akan melihat perbedaan.
Martijn
7

Jika Anda ingin mendapatkan objek yang instance Element, Anda harus menggunakan document.activeElement, tetapi jika Anda ingin mendapatkan objek yang instance Text, Anda harus menggunakandocument.getSelection().focusNode .

Semoga membantu.

rplaurindo
sumber
Lebih baik dalam hal apa?
1j01
Buka inspektur browser Anda, klik di sembarang tempat halaman, lewati ini document.getSelection().focusNode.parentElementdan ketuk Enter. Setelah itu, lewati document.activeElementdan lakukan itu samething. ;)
rplaurindo
Dengan kotak komentar ini terfokus, document.activeElementberikan <textarea>sedangkan document.getSelection().focusNodeberi <td>yang berisi <textarea>(dan document.getSelection().focusNode.parentElementberi yang <tr>berisi <td>)
1j01
Maaf, permintaan maaf saya. Saya tidak menjelaskannya dengan baik. Jika Anda ingin mendapatkan objek yang instance Element, Anda harus menggunakan document.activeElement, tetapi jika Anda ingin mendapatkan objek yang instance Text, Anda harus menggunakan document.getSelection().focusNode. Tolong, coba lagi. Saya harap saya membantu.
rplaurindo
2
Pertanyaannya adalah menanyakan elemen mana yang saat ini menjadi fokus. Dan focusNodetidak dijamin menjadi simpul teks juga.
1j01
6

Ada potensi masalah dengan menggunakan document.activeElement. Mempertimbangkan:

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

Jika pengguna berfokus pada div-dalam, maka document.activeElement masih merujuk div luar. Anda tidak dapat menggunakan document.activeElement untuk menentukan fokus div dalam mana.

Fungsi berikut menyiasatinya, dan mengembalikan simpul fokus:

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

Jika Anda lebih suka mendapatkan elemen fokus, gunakan:

function active_element(){
  var anchor = window.getSelection().anchorNode;
  if(anchor.nodeType == 3){
        return anchor.parentNode;
  }else if(anchor.nodeType == 1){
        return anchor;
  }
}
Nathan K
sumber
3
Itu tidak benar-benar masalah dengan document.activeElement: <div>elemen dalam sebenarnya tidak dapat menerima fokus, seperti yang Anda lihat secara visual dengan mengatur :focuspseudo-class ke sesuatu yang terlihat (contoh: jsfiddle.net/4gasa1t2/1 ). Yang Anda bicarakan adalah bagian dalam mana <div>yang berisi seleksi atau tanda sisipan, yang merupakan masalah terpisah.
Tim Down
6

Saya telah menemukan potongan berikut berguna ketika mencoba menentukan elemen mana yang saat ini memiliki fokus. Salin yang berikut ke konsol browser Anda, dan setiap detik akan mencetak detail elemen saat ini yang memiliki fokus.

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

Jangan ragu untuk memodifikasi console.loguntuk keluar sesuatu yang berbeda untuk membantu Anda menentukan dengan tepat elemen jika mencetak seluruh elemen tidak membantu Anda menentukan dengan tepat elemen.

vegemite4me
sumber
5

Membaca jawaban lain, dan mencoba sendiri, tampaknya document.activeElementakan memberi Anda elemen yang Anda butuhkan di sebagian besar browser.

Jika Anda memiliki browser yang tidak mendukung document.activeElement jika Anda memiliki jQuery, Anda harus dapat mengisinya pada semua acara fokus dengan sesuatu yang sangat sederhana seperti ini (belum teruji karena saya tidak memiliki browser yang memenuhi kriteria tersebut untuk diberikan. ):

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
  });
}
rjmunro
sumber
5

Jika Anda menggunakan jQuery, Anda dapat menggunakan ini untuk mengetahui apakah suatu elemen aktif:

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

Dengan dojo, Anda dapat menggunakan dijit.getFocus ()

Daniel Hartmann
sumber
3

Hanya dengan menempatkan ini di sini untuk memberikan solusi, saya akhirnya menemukan.

Saya membuat properti bernama document.activeInputArea, dan menggunakan addon HotKeys jQuery untuk menjebak acara keyboard untuk tombol panah, tab dan enter, dan saya membuat pengendali event untuk mengklik ke elemen input.

Lalu saya menyesuaikan activeInputArea setiap kali fokus berubah, jadi saya bisa menggunakan properti itu untuk mencari tahu di mana saya berada.

Sangat mudah untuk mengacaukan ini, karena jika Anda memiliki bug dalam sistem dan fokus tidak sesuai dengan yang Anda pikirkan, maka sangat sulit untuk mengembalikan fokus yang benar.

Tony Peterson
sumber