탭 인덱스에서 다음 요소에 초점 맞추기
포커스가있는 현재 요소를 기반으로 탭 시퀀스의 다음 요소로 포커스를 이동하려고합니다. 지금까지 검색에서 아무것도 찾지 못했습니다.
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;
}
}
}
나는 이것을 구현 한 적이 없지만 비슷한 문제를 조사했으며 여기에 내가 시도 할 것입니다.
먼저 시도
당신은 단순히 수 있다면 첫째, 나는 볼 것이다 화재 keypress
이벤트를 현재 포커스가있는 요소에 탭 키를. 브라우저마다이 작업을 수행하는 다른 방법이있을 수 있습니다.
그래도 안된다면 더 열심히해야합니다 ...
jQuery 구현을 참조하려면 다음을 수행해야합니다.
- Tab 및 Shift + Tab 듣기
- 탭 가능한 요소 파악
- 탭 순서 작동 방식 이해
1. Tab 및 Shift + Tab 듣기
Tab 및 Shift + Tab 듣기는 웹의 다른 곳에서 잘 다루어 질 수 있으므로 해당 부분을 건너 뛰겠습니다.
2. 탭 가능한 요소 파악
탭 가능한 요소를 아는 것은 더 까다 롭습니다. 기본적으로 요소는 포커스가 있고 속성이 tabindex="-1"
설정 되지 않은 경우 탭이 가능합니다 . 따라서 어떤 요소가 초점을 맞출 수 있는지 물어봐야합니다. 다음 요소에 초점을 맞출 수 있습니다.
input
,select
,textarea
,button
, 및object
요소 해제되지 않은.a
및 세트에 대한 숫자 값이 있거나있는area
요소 .href
tabindex
tabindex
세트에 대한 숫자 값이있는 모든 요소 .
또한 다음과 같은 경우에만 요소에 초점을 맞출 수 있습니다.
- 그 조상은 없습니다
display: none
. - 의 계산 된 값은
visibility
입니다visible
. 즉,visibility
설정 한 가장 가까운 조상 은 값이이어야합니다visible
. 조상이visibility
설정 되지 않은 경우 계산 된 값은visible
입니다.
자세한 내용은 다른 Stack Overflow 답변에 있습니다.
3. 탭 순서 작동 방식 이해
문서에있는 요소의 탭 순서는 tabindex
속성에 의해 제어됩니다 . 값이 설정되지 않은 경우은 tabindex
사실상 0
입니다.
tabindex
문서 의 순서는 1, 2, 3,…, 0입니다.
처음에 body
요소 (또는 요소 없음)에 포커스가 있을 때 탭 순서의 첫 번째 요소는 0이 아닌 가장 낮은 요소입니다 tabindex
. 여러 요소가 동일한 tabindex
경우 마지막 요소에 도달 할 때까지 문서 순서대로 이동합니다 tabindex
. 그런 다음 다음으로 낮은 값으로 이동 tabindex
하고 프로세스가 계속됩니다. 마지막으로 0 (또는 비어 있음)으로 해당 요소로 마무리 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 플러그인 을 만들었습니다 . 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"
(또는 기본적으로 탭할 수 없음)은 탭 정지 역할을하지 않습니다.- tabIndex가 동일한 두 요소의 경우 DOM에서 가장 먼저 나타나는 요소가 더 높은 우선 순위를 갖습니다.
다음은 순수 자바 스크립트를 사용하여 순서대로 탭 정지 목록을 작성하는 방법의 예입니다.
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="0"
로모그래퍼 항목 후, 목록의 맨 끝에 tabIndex
1, 2, 3 등
"enter"키를 사용하여 탭할 수있는 완전히 작동하는 예제를 보려면이 fiddle을 확인하십시오 .
위의 솔루션을 확인한 결과 상당히 길었습니다. 한 줄의 코드로 수행 할 수 있습니다.
currentElement.nextElementSibling.focus();
또는
currentElement.previousElementSibling.focus();
여기서 currentElement는 임의의 즉 document.activeElement이거나 현재 요소가 함수의 컨텍스트에있는 경우 this 일 수 있습니다.
keydown 이벤트로 탭 및 시프트 탭 이벤트를 추적했습니다.
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는 당신에게 모든 tabbable 요소의 목록 제공하는 작은 JS 패키지 탭 순서를 . 따라서 해당 목록에서 요소를 찾은 다음 다음 목록 항목에 집중할 수 있습니다.
패키지는 다른 답변에서 언급 된 복잡한 엣지 케이스를 올바르게 처리합니다 (예 : 조상이 될 수 없음 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를 사용하고 있습니까?
이것이 도움이되기를 바랍니다.
<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)" />
그런 다음 간단한 자바 스크립트를 사용하십시오.
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;
}
}
}
}
다음 요소에 초점을 맞춘보다 완전한 버전입니다. 사양 지침을 따르고 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]);
}
이것은 @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);
이것을 호출 할 수 있습니다.
탭:
$.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 플러그인을 수정 합니다.
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();
};
참고 URL : https://stackoverflow.com/questions/7208161/focus-next-element-in-tab-index
'Development Tip' 카테고리의 다른 글
FORM 내에서 DIV를 사용하는 것이 맞습니까? (0) | 2020.10.07 |
---|---|
인수로 확장되는 PROTOTYPE 매크로의 요점은 무엇입니까? (0) | 2020.10.07 |
Convert.toFloat () 메서드가없는 이유는 무엇입니까? (0) | 2020.10.07 |
데이터 속성 값을 업데이트 할 수 없습니다. (0) | 2020.10.06 |
가능한 가장 빠른 grep (0) | 2020.10.06 |