Html 文本区域中的ReactJS句柄选项卡字符

Html 文本区域中的ReactJS句柄选项卡字符,html,reactjs,Html,Reactjs,如何处理ReactJS中按下tab键的事件,以便能够缩进文本区域中的文本 当按下文本区域上的tab时,onChange事件不会被触发,因此我想可能有一个更高级别的处理程序可以用来检测此事件。您可以尝试onKeyDown并获取tab的键代码 add: function(event){ console.log(event.keyCode); //press TAB and get the keyCode }, render: function(){ return( &

如何处理ReactJS中按下tab键的事件,以便能够缩进文本区域中的文本


当按下文本区域上的tab时,onChange事件不会被触发,因此我想可能有一个更高级别的处理程序可以用来检测此事件。

您可以尝试onKeyDown并获取tab的键代码

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>
    );
}
添加:函数(事件){ console.log(event.keyCode);//按TAB键并获取keyCode }, render:function(){ 返回( ); }
您可以尝试使用KeyDown并获取选项卡的keycode

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>
    );
}
添加:函数(事件){ console.log(event.keyCode);//按TAB键并获取keyCode }, render:function(){ 返回( ); }
onKeyDown={(e:React.KeyboardEvent)=>{
如果(e.key==='Tab'&&!e.shiftKey){
e、 预防默认值();
const value=this.textareaRef.current!.value;
const selectionStart=this.textrarearef.current!.selectionStart;
const selectionEnd=this.textareaRef.current!.selectionEnd;
此.textareaRef.current!.value=
value.substring(0,selectionStart)+''+value.substring(selectionEnd);
此.textareaRef.current!.selectionStart=selectionEnd+2-(selectionEnd-selectionStart);
此.text区域引用.current!.selectionEnd=selectionEnd+2-(selectionEnd-selectionStart);
}
如果(e.key==='Tab'&&e.shiftKey){
e、 预防默认值();
const value=this.textareaRef.current!.value;
const selectionStart=this.textrarearef.current!.selectionStart;
const selectionEnd=this.textareaRef.current!.selectionEnd;
const beforeStart=值
.子字符串(0,selectionStart)
.拆分(“”)
.reverse()
.加入(“”);
常量indexOfTab=beforeStart.indexOf(“”);
const indexOfNewline=beforeStart.indexOf('\n');
if(indexOfTab!=-1&&indexOfTab
onKeyDown={(e:React.KeyboardEvent)=>{
如果(e.key==='Tab'&&!e.shiftKey){
e、 预防默认值();
const value=this.textareaRef.current!.value;
const selectionStart=this.textrarearef.current!.selectionStart;
const selectionEnd=this.textareaRef.current!.selectionEnd;
此.textareaRef.current!.value=
value.substring(0,selectionStart)+''+value.substring(selectionEnd);
此.textareaRef.current!.selectionStart=selectionEnd+2-(selectionEnd-selectionStart);
此.text区域引用.current!.selectionEnd=selectionEnd+2-(selectionEnd-selectionStart);
}
如果(e.key==='Tab'&&e.shiftKey){
e、 预防默认值();
const value=this.textareaRef.current!.value;
const selectionStart=this.textrarearef.current!.selectionStart;
const selectionEnd=this.textareaRef.current!.selectionEnd;
const beforeStart=值
.子字符串(0,selectionStart)
.拆分(“”)
.reverse()
.加入(“”);
常量indexOfTab=beforeStart.indexOf(“”);
const indexOfNewline=beforeStart.indexOf('\n');
if(indexOfTab!=-1&&indexOfTab
以防有人想在TypeScript中稍微更新(我认为是增强版)vipe解决方案:

实现使用示例:

<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 */}
/>
void,每次更改代码时调用*/}
/>
获取文本:

const txtCodeInput = useRef<EnhancedTextAreaRefs>(null);
...
const codeContent = txtCodeInput.current?.getCodeContent();
const txtCodeInput=useRef(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;
从'react'导入{forwardRef,useffect,useImportiveHandle,useRef,useState};
类型EnhancedTextAreaProps={
ContextChange?:(文本:字符串)=>void,
类名?:字符串,
拼写检查?:布尔,
tabSize?:数字,
};
导出类型EnhancedTextAreaRefs={
getCodeContent:()=>字符串;
}
常量EnhancedTextArea=forwardRef(({
onTextChange=未定义,
className=未定义,
tabSize=4,
拼写检查=false,
}:EnhancedTextAreaProps,ref)=>{
const[text,setText]=useState(“”);
常量[stateSelectionStart,setStateSelectionStart]=useState(0);
常量[stateSelectionEnd,setStateSelectionEnd]=useState(0);
const txtInput=useRef(null);
使用命令式句柄(参考,()=>({
getCodeContent:()=>文本,
}));
useffect(()=>{
const textArea=txtInput.current;
如果(!textArea){
返回;
}
如果(状态选择开始>=0){
textArea.selectionStart=状态selectionStart;
}
如果(状态选择结束>=0){
textArea.selectionEnd=状态selectionEnd;
}
},[文本,状态选择开始,状态选择结束];
异步函数handleCodeChange(e:React.ChangeEvent):承诺{
常量文本=e.target.value;
setText(文本);
if(onTextChange){
onTextChange(文本);
}
}
异步函数handleKeyDown(e:React.KeyboardEvent):承诺{
const textArea=e.target作为HtmlTextArea元素;
const tabString=''。重复(tabSize);
常量值=textArea.value;
const selectionStart=textArea.selectionStart;
const selectionEnd=textArea.selectionEnd;
如果(e.key