反应,使用键盘时无法聚焦菜单项
React, not able to focus menu item when using keyboard
上下文
所有操作都是键盘操作,没有鼠标点击。
示例:
我有一个包含文件和按钮的 2 列 table。
当在button1
上按回车键时,会弹出菜单(包含下载、查看、删除)。重点在button1
现在按下箭头键,download
背景以蓝色突出显示。
我的问题
我希望 download
按钮在按下 button1
上的向下箭头时成为 焦点 而不是 button1
完整代码:
import React, {useState, useEffect, useRef, createRef, useContext} from 'react';
const AppContext = React.createContext({
name: 'AppContext'
});
const createMenuItemRefs = (items, rowInd) => {
// obj
let menuItemRefs = {};
// loop each
for (let i = 0; i < Object.keys(items).length; i++) {
// When assign createRef, no current
menuItemRefs[rowInd + i] = createRef();
}
return menuItemRefs;
};
function Menu({buttonName, parentRowIndex}) {
const [currRowInd, setCurrRowInd] = useState('');
const [open, setOpen] = useState(false);
// press down key, will get 1st item which at index 0
const [menuItemActiveIndex, setMenuItemActiveIndex] = useState(-1);
const menuItems = {download: 'download', view: 'view', delete: 'delete'};
const menuItemRefs = useRef(createMenuItemRefs(menuItems, parentRowIndex));
useEffect(() => {
if (open && menuItemActiveIndex >= 0 && parentRowIndex !== '') {
menuItemRefs.current[parentRowIndex + menuItemActiveIndex].focus();
}
}, [menuItemActiveIndex, open, parentRowIndex]);
// on the button level
const buttonIconKeyDown = (event, parentRowIndex) => {
if (event.keyCode === 13) {
// Enter pressed
console.log('enter is pressed');
setOpen(!open);
setCurrRowInd(parentRowIndex);
} else if (event.keyCode === 9) {
// tab away
console.log('tab away');
setOpen(!open);
setCurrRowInd('');
} else if (event.keyCode === 40) {
//test
console.log('down arrow');
// 38 is up arrow
// No scrolling
event.preventDefault();
// set to 1st item in 0 index
setMenuItemActiveIndex(0);
}
};
//test
console.log(
'menuItemRefs.current',
menuItemRefs.current,
'menuItemActiveIndex',
menuItemActiveIndex
);
return (
<div>
<button
onKeyDown={event => {
//test
console.log('parent buttonicon onkeydown: ');
buttonIconKeyDown(event, parentRowIndex);
}}
>
{buttonName}
</button>
{open && parentRowIndex === currRowInd && (
<ul style={{padding: '5px', margin: '10px', border: '1px solid #ccc'}}>
{Object.keys(menuItems).map((item, itemIndex) => {
if (itemIndex === menuItemActiveIndex)
return (
<li
key={itemIndex}
style={{
listStyle: 'none',
padding: '5px',
backgroundColor: 'blue'
}}
// put a ref
ref={element =>
(menuItemRefs.current[parentRowIndex + itemIndex] = element)
}
>
<button>{item}</button>
</li>
);
else
return (
<li
key={itemIndex}
style={{listStyle: 'none', padding: '5px'}}
ref={element =>
(menuItemRefs.current[parentRowIndex + itemIndex] = element)
}
>
<button>{item}</button>
</li>
);
})}
</ul>
)}
</div>
);
}
function TableElement() {
const items = [
{
file: 'file1',
button: 'button1'
},
{
file: 'file2',
button: 'button2'
},
{
file: 'file3',
button: 'button3'
}
];
return (
<table style={{borderCollapse: 'collapse', border: '1px solid black'}}>
<tbody>
{items.map((item, index) => {
return (
<tr key={index}>
<td style={{border: '1px solid black'}}>
<a href="#">{item.file}</a>
</td>
<td style={{border: '1px solid black'}}>
<Menu buttonName={item.button} parentRowIndex={index} />
</td>
</tr>
);
})}
</tbody>
</table>
);
}
function App() {
const appContextObj = {};
return (
<>
<AppContext.Provider value={appContextObj}>
<TableElement />
</AppContext.Provider>
</>
);
}
export default App;
github
https://github.com/kenpeter/key-mouse-dropdown/tree/feature/focus
在你的useEffect
块
useEffect(() => {
if (open && menuItemActiveIndex >= 0 && parentRowIndex !== '') {
menuItemRefs.current[parentRowIndex + menuItemActiveIndex].children[0].focus();
}
}, [menuItemActiveIndex, open, parentRowIndex]);
在这种情况下,您可以将注意力集中在您的 children 上 <button>
,而不是将注意力集中在您的 <li>
上。请注意,如果上面的代码将始终关注您的 ref 当前实例的第一个 child。
奖金:
您还可以在任何元素上使用 tabIndex={-1}
来防止它被聚焦。
上下文
所有操作都是键盘操作,没有鼠标点击。
示例:
我有一个包含文件和按钮的 2 列 table。
当在button1
上按回车键时,会弹出菜单(包含下载、查看、删除)。重点在button1
现在按下箭头键,download
背景以蓝色突出显示。
我的问题
我希望 download
按钮在按下 button1
button1
完整代码:
import React, {useState, useEffect, useRef, createRef, useContext} from 'react';
const AppContext = React.createContext({
name: 'AppContext'
});
const createMenuItemRefs = (items, rowInd) => {
// obj
let menuItemRefs = {};
// loop each
for (let i = 0; i < Object.keys(items).length; i++) {
// When assign createRef, no current
menuItemRefs[rowInd + i] = createRef();
}
return menuItemRefs;
};
function Menu({buttonName, parentRowIndex}) {
const [currRowInd, setCurrRowInd] = useState('');
const [open, setOpen] = useState(false);
// press down key, will get 1st item which at index 0
const [menuItemActiveIndex, setMenuItemActiveIndex] = useState(-1);
const menuItems = {download: 'download', view: 'view', delete: 'delete'};
const menuItemRefs = useRef(createMenuItemRefs(menuItems, parentRowIndex));
useEffect(() => {
if (open && menuItemActiveIndex >= 0 && parentRowIndex !== '') {
menuItemRefs.current[parentRowIndex + menuItemActiveIndex].focus();
}
}, [menuItemActiveIndex, open, parentRowIndex]);
// on the button level
const buttonIconKeyDown = (event, parentRowIndex) => {
if (event.keyCode === 13) {
// Enter pressed
console.log('enter is pressed');
setOpen(!open);
setCurrRowInd(parentRowIndex);
} else if (event.keyCode === 9) {
// tab away
console.log('tab away');
setOpen(!open);
setCurrRowInd('');
} else if (event.keyCode === 40) {
//test
console.log('down arrow');
// 38 is up arrow
// No scrolling
event.preventDefault();
// set to 1st item in 0 index
setMenuItemActiveIndex(0);
}
};
//test
console.log(
'menuItemRefs.current',
menuItemRefs.current,
'menuItemActiveIndex',
menuItemActiveIndex
);
return (
<div>
<button
onKeyDown={event => {
//test
console.log('parent buttonicon onkeydown: ');
buttonIconKeyDown(event, parentRowIndex);
}}
>
{buttonName}
</button>
{open && parentRowIndex === currRowInd && (
<ul style={{padding: '5px', margin: '10px', border: '1px solid #ccc'}}>
{Object.keys(menuItems).map((item, itemIndex) => {
if (itemIndex === menuItemActiveIndex)
return (
<li
key={itemIndex}
style={{
listStyle: 'none',
padding: '5px',
backgroundColor: 'blue'
}}
// put a ref
ref={element =>
(menuItemRefs.current[parentRowIndex + itemIndex] = element)
}
>
<button>{item}</button>
</li>
);
else
return (
<li
key={itemIndex}
style={{listStyle: 'none', padding: '5px'}}
ref={element =>
(menuItemRefs.current[parentRowIndex + itemIndex] = element)
}
>
<button>{item}</button>
</li>
);
})}
</ul>
)}
</div>
);
}
function TableElement() {
const items = [
{
file: 'file1',
button: 'button1'
},
{
file: 'file2',
button: 'button2'
},
{
file: 'file3',
button: 'button3'
}
];
return (
<table style={{borderCollapse: 'collapse', border: '1px solid black'}}>
<tbody>
{items.map((item, index) => {
return (
<tr key={index}>
<td style={{border: '1px solid black'}}>
<a href="#">{item.file}</a>
</td>
<td style={{border: '1px solid black'}}>
<Menu buttonName={item.button} parentRowIndex={index} />
</td>
</tr>
);
})}
</tbody>
</table>
);
}
function App() {
const appContextObj = {};
return (
<>
<AppContext.Provider value={appContextObj}>
<TableElement />
</AppContext.Provider>
</>
);
}
export default App;
github https://github.com/kenpeter/key-mouse-dropdown/tree/feature/focus
在你的useEffect
块
useEffect(() => {
if (open && menuItemActiveIndex >= 0 && parentRowIndex !== '') {
menuItemRefs.current[parentRowIndex + menuItemActiveIndex].children[0].focus();
}
}, [menuItemActiveIndex, open, parentRowIndex]);
在这种情况下,您可以将注意力集中在您的 children 上 <button>
,而不是将注意力集中在您的 <li>
上。请注意,如果上面的代码将始终关注您的 ref 当前实例的第一个 child。
奖金:
您还可以在任何元素上使用 tabIndex={-1}
来防止它被聚焦。