フォーカスがある現在の要素に基づいて、タブシーケンス内の次の要素にフォーカスを移動しようとしています。これまでのところ、検索で何も表示していません。
function OnFocusOut()
{
var currentElement = $get(currentElementId); // ID set by OnFocusIn
currentElementId = "";
currentElement.nextElementByTabIndex.focus();
}
もちろん、nextElementByTabIndexは、これが機能するための重要な部分です。タブシーケンスの次の要素を見つけるにはどうすればよいですか?ソリューションは、JQueryのようなものではなく、JScriptを使用する必要があります。
Jqueryを使用しない場合:まず、タブ可能な要素にclass="tabable"
を追加します。これにより、後で選択できるようになります。 (以下のコードの「。」クラスセレクタプレフィックスを忘れないでください)
var lastTabIndex = 10;
function OnFocusOut()
{
var currentElement = $get(currentElementId); // ID set by OnFOcusIn
var curIndex = currentElement.tabIndex; //get current elements tab index
if(curIndex == lastTabIndex) { //if we are on the last tabindex, go back to the beginning
curIndex = 0;
}
var tabbables = document.querySelectorAll(".tabable"); //get all tabable elements
for(var i=0; i<tabbables.length; i++) { //loop through each element
if(tabbables[i].tabIndex == (curIndex+1)) { //check the tabindex to see if it's the element we want
tabbables[i].focus(); //if it's the one we want, focus it and exit the loop
break;
}
}
}
私はこれを実装したことはありませんが、私は同様の問題を調べました。
最初に、現在フォーカスがある要素のTabキーに対して keypress
イベントを発生させる ができるかどうかを確認します。ブラウザごとにこれを行う別の方法があるかもしれません。
JQuery実装を参照するには、以下を行う必要があります。
TabおよびShift + Tabのリッスンは、おそらくWebの他の場所で十分にカバーされているので、その部分はスキップします。
どの要素がタブ化可能であるかを知ることは、より複雑です。基本的に、要素はフォーカス可能で、属性tabindex="-1"
が設定されていない場合、タブ可能です。そのため、どの要素がフォーカス可能かを尋ねる必要があります。次の要素はフォーカス可能です。
input
、select
、textarea
、button
、およびobject
要素。a
またはarea
の数値が設定されているhref
およびtabindex
要素。tabindex
の数値が設定されている要素。さらに、要素は次の場合にのみフォーカス可能です。
display: none
ではありません。visibility
の計算値はvisible
です。つまり、visibility
を設定する最も近い祖先の値はvisible
でなければなりません。祖先にvisibility
が設定されていない場合、計算される値はvisible
です。詳細は別の Stack Overflow answer にあります。
文書内の要素のタブ順序は、tabindex
属性によって制御されます。値が設定されていない場合、tabindex
は実質的に0
です。
ドキュメントのtabindex
順序は、1、2、3、…、0です。
最初に、body
要素(または要素なし)にフォーカスがある場合、タブオーダーの最初の要素は、ゼロ以外の最小のtabindex
です。複数の要素が同じtabindex
を持っている場合、そのtabindex
を持つ最後の要素に到達するまでドキュメントの順序で進みます。次に、次に低いtabindex
に移動し、プロセスを続行します。最後に、これらの要素をゼロ(または空)tabindex
で終了します。
以下は、この目的のために作成したものです。
focusNextElement: function () {
//add all elements we want to include in our selection
var focussableElements = 'a:not([disabled]), button:not([disabled]), input[type=text]:not([disabled]), [tabindex]:not([disabled]):not([tabindex="-1"])';
if (document.activeElement && document.activeElement.form) {
var focussable = Array.prototype.filter.call(document.activeElement.form.querySelectorAll(focussableElements),
function (element) {
//check for visibility while always include the current activeElement
return element.offsetWidth > 0 || element.offsetHeight > 0 || element === document.activeElement
});
var index = focussable.indexOf(document.activeElement);
if(index > -1) {
var nextElement = focussable[index + 1] || focussable[0];
nextElement.focus();
}
}
}
シンプルなjQueryプラグイン を作成しました。 jQuery UIの「:tabbable」セレクターを使用して、次の「tabbable」要素を見つけて選択します。
使用例:
// Simulate tab key when element is clicked
$('.myElement').bind('click', function(event){
$.tabNext();
return false;
});
答えの中核は、次の要素を見つけることにあります。
function findNextTabStop(el) {
var universe = document.querySelectorAll('input, button, select, textarea, a[href]');
var list = Array.prototype.filter.call(universe, function(item) {return item.tabIndex >= "0"});
var index = list.indexOf(el);
return list[index + 1] || list[0];
}
使用法:
var nextEl = findNextTabStop(element);
nextEl.focus();
tabIndex
の優先順位を気にしないことに注意してください。
上記のコメントで述べたように、ブラウザがタブ順序情報を公開することはないと思います。ここでは、タブオーダーで次の要素を取得するためにブラウザーが実行する処理の簡略化された近似を示します。
var allowedTags = {input: true, textarea: true, button: true};
var walker = document.createTreeWalker(
document.body,
NodeFilter.SHOW_ELEMENT,
{
acceptNode: function(node)
{
if (node.localName in allowedTags)
return NodeFilter.FILTER_ACCEPT;
else
NodeFilter.FILTER_SKIP;
}
},
false
);
walker.currentNode = currentElement;
if (!walker.nextNode())
{
// Restart search from the start of the document
walker.currentNode = walker.root;
walker.nextNode();
}
if (walker.currentNode && walker.currentNode != walker.root)
walker.currentNode.focus();
これはいくつかのタグのみを考慮し、tabindex
属性を無視しますが、何を達成しようとしているかによっては十分かもしれません。
要素のtabIndex
プロパティをチェックして、フォーカス可能であるかどうかを判断できるようです。フォーカス可能でない要素のtabindex
の値は「-1」です。
次に、タブストップのルールを知る必要があります:
tabIndex="1"
の優先度が最も高くなっています。tabIndex="2"
が次に高い優先度を持ちます。tabIndex="3"
が次に続きます。tabIndex="0"
(またはデフォルトでタブ移動可能)の優先度は最低です。tabIndex="-1"
(またはデフォルトではタブ移動不可)はタブストップとして機能しません。純粋なJavascriptを使用して、タブストップのリストを順番に作成する方法の例を次に示します。
function getTabStops(o, a, el) {
// Check if this element is a tab stop
if (el.tabIndex > 0) {
if (o[el.tabIndex]) {
o[el.tabIndex].Push(el);
} else {
o[el.tabIndex] = [el];
}
} else if (el.tabIndex === 0) {
// Tab index "0" comes last so we accumulate it seperately
a.Push(el);
}
// Check if children are tab stops
for (var i = 0, l = el.children.length; i < l; i++) {
getTabStops(o, a, el.children[i]);
}
}
var o = [],
a = [],
stops = [],
active = document.activeElement;
getTabStops(o, a, document.body);
// Use simple loops for maximum browser support
for (var i = 0, l = o.length; i < l; i++) {
if (o[i]) {
for (var j = 0, m = o[i].length; j < m; j++) {
stops.Push(o[i][j]);
}
}
}
for (var i = 0, l = a.length; i < l; i++) {
stops.Push(a[i]);
}
最初にDOMを調べて、すべてのタブストップをインデックスと順番に収集します。次に、最終リストを作成します。 tabIndex
が1、2、3などのアイテムの後に、リストの最後にtabIndex="0"
のアイテムを追加することに注意してください。
「Enter」キーを使用してタブ移動できる完全に機能する例については、この fiddle を確認してください。
上記の解決策を確認しましたが、かなり時間がかかりました。これは、たった1行のコードで実現できます。
currentElement.nextElementSibling.focus();
または
currentElement.previousElementSibling.focus();
ここで、currentElementはdocument.activeElementのいずれか、または現在の要素が関数のコンテキストにある場合はthisになります。
キーダウンイベントでタブイベントとシフトタブイベントを追跡しました
let cursorDirection = ''
$(document).keydown(function (e) {
let key = e.which || e.keyCode;
if (e.shiftKey) {
//does not matter if user has pressed tab key or not.
//If it matters for you then compare it with 9
cursorDirection = 'prev';
}
else if (key == 9) {
//if tab key is pressed then move next.
cursorDirection = 'next';
}
else {
cursorDirection == '';
}
});
カーソルの方向が決まったら、nextElementSibling.focus
またはpreviousElementSibling.focus
メソッドを使用できます
Tabbable は、すべてのタブ可能な要素のリストをタブ順で提供する小さなJSパッケージです。そのため、そのリスト内で要素を見つけて、次のリストエントリに焦点を当てることができます。
このパッケージは、他の回答に記載されている複雑なEdgeのケースを正しく処理します(たとえば、祖先がdisplay: none
になることはありません)。そして、jQueryに依存しません!
この記事の執筆時点(バージョン1.1.1)では、IE8をサポートしておらず、ブラウザーのバグによりcontenteditable
を正しく処理できないという警告があります。
これがSOの最初の投稿なので、受け入れられた答えにコメントするほどの評判はありませんが、コードを次のように変更する必要がありました。
export function focusNextElement () {
//add all elements we want to include in our selection
const focussableElements =
'a:not([disabled]), button:not([disabled]), input[type=text]:not([disabled])'
if (document.activeElement && document.activeElement.form) {
var focussable = Array.prototype.filter.call(
document.activeElement.form.querySelectorAll(focussableElements),
function (element) {
// if element has tabindex = -1, it is not focussable
if ( element.hasAttribute('tabindex') && element.tabIndex === -1 ){
return false
}
//check for visibility while always include the current activeElement
return (element.offsetWidth > 0 || element.offsetHeight > 0 ||
element === document.activeElement)
});
console.log(focussable)
var index = focussable.indexOf(document.activeElement);
if(index > -1) {
var nextElement = focussable[index + 1] || focussable[0];
console.log(nextElement)
nextElement.focus()
}
}
}
Varを定数に変更することは重要ではありません。主な変更点は、tabindex!= "-1"をチェックするセレクターを取り除くことです。その後、要素にtabindex属性があり、かつ「-1」に設定されている場合、フォーカス可能とは見なされません。
これを変更する必要がある理由は、tabindex = "-1"を<input>
に追加するときに、この要素が「input [type = text]:not([disabled])」と一致するため、フォーカス可能と見なされたためです。セレクタ。私の変更は、「無効化されていないテキスト入力であり、tabIndex属性があり、その属性の値が-1である場合、フォーカス可能と見なされるべきではない」と同等です。
受け入れられた回答の作成者がtabIndex属性を説明するために回答を編集したときに、正しく編集しなかったと思います。そうでない場合はお知らせください
循環させる各要素に独自のtabIndex値を指定しましたか?もしそうなら、あなたはこれを試すことができます:
var lasTabIndex = 10; //Set this to the highest tabIndex you have
function OnFocusOut()
{
var currentElement = $get(currentElementId); // ID set by OnFocusIn
var curIndex = $(currentElement).attr('tabindex'); //get the tab index of the current element
if(curIndex == lastTabIndex) { //if we are on the last tabindex, go back to the beginning
curIndex = 0;
}
$('[tabindex=' + (curIndex + 1) + ']').focus(); //set focus on the element that has a tab index one greater than the current tab index
}
Jqueryを使用していますよね?
function focusNextElement(){
var focusable = [].slice.call(document.querySelectorAll("a, button, input, select, textarea, [tabindex], [contenteditable]")).filter(function($e){
if($e.disabled || ($e.getAttribute("tabindex") && parseInt($e.getAttribute("tabindex"))<0)) return false;
return true;
}).sort(function($a, $b){
return (parseFloat($a.getAttribute("tabindex") || 99999) || 99999) - (parseFloat($b.getAttribute("tabindex") || 99999) || 99999);
});
var focusIndex = focusable.indexOf(document.activeElement);
if(focusable[focusIndex+1]) focusable[focusIndex+1].focus();
};
これは、@ Kanoおよび@ Mxが提供する優れたソリューションに対する潜在的な拡張機能です。 TabIndexの順序を保持する場合は、このソートを中央に追加します。
// Sort by explicit Tab Index, if any
var sort_by_TabIndex = function (elementA, elementB) {
let a = elementA.tabIndex || 1;
let b = elementB.tabIndex || 1;
if (a < b) { return -1; }
if (a > b) { return 1; }
return 0;
}
focussable.sort(sort_by_TabIndex);
次は、次の要素に焦点を当てたより完全なバージョンです。仕様のガイドラインに従い、tabindexを使用して要素のリストを正しく並べ替えます。また、前の要素を取得する場合は、逆変数が定義されます。
function focusNextElement( reverse, activeElem ) {
/*check if an element is defined or use activeElement*/
activeElem = activeElem instanceof HTMLElement ? activeElem : document.activeElement;
let queryString = [
'a:not([disabled]):not([tabindex="-1"])',
'button:not([disabled]):not([tabindex="-1"])',
'input:not([disabled]):not([tabindex="-1"])',
'select:not([disabled]):not([tabindex="-1"])',
'[tabindex]:not([disabled]):not([tabindex="-1"])'
/* add custom queries here */
].join(','),
queryResult = Array.prototype.filter.call(document.querySelectorAll(queryString), elem => {
/*check for visibility while always include the current activeElement*/
return elem.offsetWidth > 0 || elem.offsetHeight > 0 || elem === activeElem;
}),
indexedList = queryResult.slice().filter(elem => {
/* filter out all indexes not greater than 0 */
return elem.tabIndex == 0 || elem.tabIndex == -1 ? false : true;
}).sort((a, b) => {
/* sort the array by index from smallest to largest */
return a.tabIndex != 0 && b.tabIndex != 0
? (a.tabIndex < b.tabIndex ? -1 : b.tabIndex < a.tabIndex ? 1 : 0)
: a.tabIndex != 0 ? -1 : b.tabIndex != 0 ? 1 : 0;
}),
focusable = [].concat(indexedList, queryResult.filter(elem => {
/* filter out all indexes above 0 */
return elem.tabIndex == 0 || elem.tabIndex == -1 ? true : false;
}));
/* if reverse is true return the previous focusable element
if reverse is false return the next focusable element */
return reverse ? (focusable[focusable.indexOf(activeElem) - 1] || focusable[focusable.length - 1])
: (focusable[focusable.indexOf(activeElem) + 1] || focusable[0]);
}
これが役に立てば幸いです。
<input size="2" tabindex="1" id="one"
maxlength="2" onkeyup="toUnicode(this)" />
<input size="2" tabindex="2" id="two"
maxlength="2" onkeyup="toUnicode(this)" />
<input size="2" tabindex="3" id="three"
maxlength="2" onkeyup="toUnicode(this)" />
次に、シンプルなjavascriptを使用します
function toUnicode(elmnt)
{
var next;
if (elmnt.value.length==elmnt.maxLength)
{
next=elmnt.tabIndex + 1;
//look for the fields with the next tabIndex
var f = elmnt.form;
for (var i = 0; i < f.elements.length; i++)
{
if (next<=f.elements[i].tabIndex)
{
f.elements[i].focus();
break;
}
}
}
}
これを呼び出すことができます:
タブ:
$.tabNext();
Shift + Tab:
$.tabPrev();
<!DOCTYPE html>
<html>
<body>
<script src="https://code.jquery.com/jquery-3.3.1.js" integrity="sha256-2Kok7MbOyxpgUVvAk/HJ2jigOSYS2auK4Pfzbm7uH60=" crossorigin="anonymous"></script>
<script>
(function($){
'use strict';
/**
* Focusses the next :focusable element. Elements with tabindex=-1 are focusable, but not tabable.
* Does not take into account that the taborder might be different as the :tabbable elements order
* (which happens when using tabindexes which are greater than 0).
*/
$.focusNext = function(){
selectNextTabbableOrFocusable(':focusable');
};
/**
* Focusses the previous :focusable element. Elements with tabindex=-1 are focusable, but not tabable.
* Does not take into account that the taborder might be different as the :tabbable elements order
* (which happens when using tabindexes which are greater than 0).
*/
$.focusPrev = function(){
selectPrevTabbableOrFocusable(':focusable');
};
/**
* Focusses the next :tabable element.
* Does not take into account that the taborder might be different as the :tabbable elements order
* (which happens when using tabindexes which are greater than 0).
*/
$.tabNext = function(){
selectNextTabbableOrFocusable(':tabbable');
};
/**
* Focusses the previous :tabbable element
* Does not take into account that the taborder might be different as the :tabbable elements order
* (which happens when using tabindexes which are greater than 0).
*/
$.tabPrev = function(){
selectPrevTabbableOrFocusable(':tabbable');
};
function tabIndexToInt(tabIndex){
var tabIndexInded = parseInt(tabIndex);
if(isNaN(tabIndexInded)){
return 0;
}else{
return tabIndexInded;
}
}
function getTabIndexList(elements){
var list = [];
for(var i=0; i<elements.length; i++){
list.Push(tabIndexToInt(elements.eq(i).attr("tabIndex")));
}
return list;
}
function selectNextTabbableOrFocusable(selector){
var selectables = $(selector);
var current = $(':focus');
// Find same TabIndex of remainder element
var currentIndex = selectables.index(current);
var currentTabIndex = tabIndexToInt(current.attr("tabIndex"));
for(var i=currentIndex+1; i<selectables.length; i++){
if(tabIndexToInt(selectables.eq(i).attr("tabIndex")) === currentTabIndex){
selectables.eq(i).focus();
return;
}
}
// Check is last TabIndex
var tabIndexList = getTabIndexList(selectables).sort(function(a, b){return a-b});
if(currentTabIndex === tabIndexList[tabIndexList.length-1]){
currentTabIndex = -1;// Starting from 0
}
// Find next TabIndex of all element
var nextTabIndex = tabIndexList.find(function(element){return currentTabIndex<element;});
for(var i=0; i<selectables.length; i++){
if(tabIndexToInt(selectables.eq(i).attr("tabIndex")) === nextTabIndex){
selectables.eq(i).focus();
return;
}
}
}
function selectPrevTabbableOrFocusable(selector){
var selectables = $(selector);
var current = $(':focus');
// Find same TabIndex of remainder element
var currentIndex = selectables.index(current);
var currentTabIndex = tabIndexToInt(current.attr("tabIndex"));
for(var i=currentIndex-1; 0<=i; i--){
if(tabIndexToInt(selectables.eq(i).attr("tabIndex")) === currentTabIndex){
selectables.eq(i).focus();
return;
}
}
// Check is last TabIndex
var tabIndexList = getTabIndexList(selectables).sort(function(a, b){return b-a});
if(currentTabIndex <= tabIndexList[tabIndexList.length-1]){
currentTabIndex = tabIndexList[0]+1;// Starting from max
}
// Find prev TabIndex of all element
var prevTabIndex = tabIndexList.find(function(element){return element<currentTabIndex;});
for(var i=selectables.length-1; 0<=i; i--){
if(tabIndexToInt(selectables.eq(i).attr("tabIndex")) === prevTabIndex){
selectables.eq(i).focus();
return;
}
}
}
/**
* :focusable and :tabbable, both taken from jQuery UI Core
*/
$.extend($.expr[ ':' ], {
data: $.expr.createPseudo ?
$.expr.createPseudo(function(dataName){
return function(elem){
return !!$.data(elem, dataName);
};
}) :
// support: jQuery <1.8
function(elem, i, match){
return !!$.data(elem, match[ 3 ]);
},
focusable: function(element){
return focusable(element, !isNaN($.attr(element, 'tabindex')));
},
tabbable: function(element){
var tabIndex = $.attr(element, 'tabindex'),
isTabIndexNaN = isNaN(tabIndex);
return ( isTabIndexNaN || tabIndex >= 0 ) && focusable(element, !isTabIndexNaN);
}
});
/**
* focussable function, taken from jQuery UI Core
* @param element
* @returns {*}
*/
function focusable(element){
var map, mapName, img,
nodeName = element.nodeName.toLowerCase(),
isTabIndexNotNaN = !isNaN($.attr(element, 'tabindex'));
if('area' === nodeName){
map = element.parentNode;
mapName = map.name;
if(!element.href || !mapName || map.nodeName.toLowerCase() !== 'map'){
return false;
}
img = $('img[usemap=#' + mapName + ']')[0];
return !!img && visible(img);
}
return ( /^(input|select|textarea|button|object)$/.test(nodeName) ?
!element.disabled :
'a' === nodeName ?
element.href || isTabIndexNotNaN :
isTabIndexNotNaN) &&
// the element and all of its ancestors must be visible
visible(element);
function visible(element){
return $.expr.filters.visible(element) && !$(element).parents().addBack().filter(function(){
return $.css(this, 'visibility') === 'hidden';
}).length;
}
}
})(jQuery);
</script>
<a tabindex="5">5</a><br>
<a tabindex="20">20</a><br>
<a tabindex="3">3</a><br>
<a tabindex="7">7</a><br>
<a tabindex="20">20</a><br>
<a tabindex="0">0</a><br>
<script>
var timer;
function tab(){
window.clearTimeout(timer)
timer = window.setInterval(function(){$.tabNext();}, 1000);
}
function shiftTab(){
window.clearTimeout(timer)
timer = window.setInterval(function(){$.tabPrev();}, 1000);
}
</script>
<button tabindex="-1" onclick="tab()">Tab</button>
<button tabindex="-1" onclick="shiftTab()">Shift+Tab</button>
</body>
</html>
jquery.tabbable PlugInを変更して完了します。