如何在ReactJS中处理Tab键按下的事件,以便我能够在textarea中缩进文本?
当在textarea上按下tab时,onChange事件不会被触发,所以我猜可能有一个更高级别的处理程序可以用来检测这个事件。
答案 0 :(得分:1)
您可以尝试onKeyDown并获取标签的密钥代码。
add: function(event){
console.log(event.keyCode); //press TAB and get the keyCode
},
render: function(){
return(
<div>
<input type="text" id="one" onKeyDown={this.add} />
</div>
);
}
答案 1 :(得分:0)
onKeyDown={(e: React.KeyboardEvent) => {
if (e.key === 'Tab' && !e.shiftKey) {
e.preventDefault();
const value = this.textareaRef.current!.value;
const selectionStart = this.textareaRef.current!.selectionStart;
const selectionEnd = this.textareaRef.current!.selectionEnd;
this.textareaRef.current!.value =
value.substring(0, selectionStart) + ' ' + value.substring(selectionEnd);
this.textareaRef.current!.selectionStart = selectionEnd + 2 - (selectionEnd - selectionStart);
this.textareaRef.current!.selectionEnd = selectionEnd + 2 - (selectionEnd - selectionStart);
}
if (e.key === 'Tab' && e.shiftKey) {
e.preventDefault();
const value = this.textareaRef.current!.value;
const selectionStart = this.textareaRef.current!.selectionStart;
const selectionEnd = this.textareaRef.current!.selectionEnd;
const beforeStart = value
.substring(0, selectionStart)
.split('')
.reverse()
.join('');
const indexOfTab = beforeStart.indexOf(' ');
const indexOfNewline = beforeStart.indexOf('\n');
if (indexOfTab !== -1 && indexOfTab < indexOfNewline) {
this.textareaRef.current!.value =
beforeStart
.substring(indexOfTab + 2)
.split('')
.reverse()
.join('') +
beforeStart
.substring(0, indexOfTab)
.split('')
.reverse()
.join('') +
value.substring(selectionEnd);
this.textareaRef.current!.selectionStart = selectionStart - 2;
this.textareaRef.current!.selectionEnd = selectionEnd - 2;
}
}
}}
答案 2 :(得分:0)
以防万一有人想要在 TypeScript 中稍微更新和(在我看来是增强的)vipe 解决方案的 React Hooks 版本:
实现用法示例:
<EnhancedTextArea
ref={txtCodeInput} {/* reference used whenever required as seen below */}
className='code-input'
tabSize={2}
onTextChange={handleCodeChange} {/* Function accepting callback of type (string) -> void, called every time code is changed */}
/>
获取文本:
const txtCodeInput = useRef<EnhancedTextAreaRefs>(null);
...
const codeContent = txtCodeInput.current?.getCodeContent();
EnhancedTextArea.tsx:
import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react';
type EnhancedTextAreaProps = {
onTextChange?: (text: string) => void,
className?: string,
spellCheck?: boolean,
tabSize?: number,
};
export type EnhancedTextAreaRefs = {
getCodeContent: () => string;
}
const EnhancedTextArea = forwardRef<EnhancedTextAreaRefs, EnhancedTextAreaProps>(({
onTextChange = undefined,
className = undefined,
tabSize = 4,
spellCheck = false,
}: EnhancedTextAreaProps, ref) => {
const [text, setText] = useState('');
const [stateSelectionStart, setStateSelectionStart] = useState(0);
const [stateSelectionEnd, setStateSelectionEnd] = useState(0);
const txtInput = useRef<HTMLTextAreaElement>(null);
useImperativeHandle(ref, () => ({
getCodeContent: () => text,
}));
useEffect(() => {
const textArea = txtInput.current;
if (!textArea) {
return;
}
if (stateSelectionStart >= 0) {
textArea.selectionStart = stateSelectionStart;
}
if (stateSelectionEnd >= 0) {
textArea.selectionEnd = stateSelectionEnd;
}
}, [text, stateSelectionStart, stateSelectionEnd]);
async function handleCodeChange(e: React.ChangeEvent<HTMLTextAreaElement>): Promise<void> {
const text = e.target.value;
setText(text);
if (onTextChange) {
onTextChange(text);
}
}
async function handleKeyDown(e: React.KeyboardEvent<HTMLTextAreaElement>): Promise<void> {
const textArea = e.target as HTMLTextAreaElement;
const tabString = ' '.repeat(tabSize);
const value = textArea.value;
const selectionStart = textArea.selectionStart;
const selectionEnd = textArea.selectionEnd;
if (e.key === 'Tab' && !e.shiftKey) {
e.preventDefault();
if (selectionStart !== selectionEnd) {
const slices1 = getNewLineSlices(value, selectionStart, selectionEnd);
const newValue1 = addTabs(value, slices1, tabString);
setText(newValue1);
setStateSelectionStart(selectionStart + tabSize);
setStateSelectionEnd(selectionEnd + (newValue1.length - value.length));
} else {
const newValue2 = value.substring(0, selectionStart) + tabString + value.substring(selectionEnd);
setText(newValue2);
setStateSelectionStart(selectionEnd + tabSize - (selectionEnd - selectionStart));
setStateSelectionEnd(selectionEnd + tabSize - (selectionEnd - selectionStart));
}
} else if (e.key === 'Tab' && e.shiftKey) {
e.preventDefault();
const slices2 = getNewLineSlices(value, selectionStart, selectionEnd);
const newValue3 = removeTabs(value, slices2, tabSize);
const diff = value.length - newValue3.length;
setText(newValue3);
setStateSelectionStart(Math.max(0, selectionStart - (diff ? tabSize : 0)));
setStateSelectionEnd(Math.max(0, selectionEnd - diff));
} else {
setStateSelectionStart(-1);
setStateSelectionEnd(-1);
}
}
function getNewLineSlices(value: string, selectionStart: number, selectionEnd: number): Array<string | null> {
const newLineLocations = getAllIndices(value, '\n');
const left = findRange(newLineLocations, selectionStart);
const split = value.split('\n');
const arr = [];
let count = 0;
for (let i = 0; i < split.length; i++) {
const line = split[i];
if (count > left && count <= selectionEnd) {
arr.push(line);
} else {
arr.push(null);
}
count += line.length + 1;
}
return arr;
}
function addTabs(value: string, arr: Array<string | null>, joiner: string): string {
const split = value.split('\n');
let ret = '';
for (let i = 0; i < split.length; i++) {
const val = split[i];
const newLineVal = arr[i];
if (newLineVal === val) {
ret += joiner;
}
ret += val;
if (i !== split.length - 1) {
ret += '\n';
}
}
return ret;
}
function removeTabs(value: string, arr: Array<string | null>, tabSize: number): string {
const split = value.split('\n');
let ret = '';
for (let i = 0; i < split.length; i++) {
const val = split[i];
const newLineVal = arr[i];
if (!val.startsWith(' ') || newLineVal !== val) {
ret += val;
if (i !== split.length - 1) {
ret += '\n';
}
continue;
}
let count = 1;
while (val[count] === ' ' && count < tabSize) {
count++;
}
ret += val.substring(count);
if (i !== split.length - 1) {
ret += '\n';
}
}
return ret;
}
function getAllIndices(arr: string, val: string): Array<number> {
const indices = [];
let i = -1;
while ((i = arr.indexOf(val, i + 1)) !== -1){
indices.push(i);
}
return indices;
}
function findRange(arr: Array<number>, min: number): number {
for (let i = 0; i < arr.length; i++) {
if (arr[i] >= min) {
return i === 0 ? -1 : arr[i - 1];
}
}
return arr[arr.length - 1];
}
return(
<textarea
ref={txtInput}
value={text}
onKeyDown={handleKeyDown}
onChange={handleCodeChange}
className={className}
spellCheck={spellCheck} />
);
});
EnhancedTextArea.displayName = 'EnhancedTextArea';
export default EnhancedTextArea;