首页 > 解决方案 > 在特定索引处突出显示 React 中 @material-ui/core TextField 的值

问题描述

我正在研究 React,并使用来自 @material-ui/core 的 TextField 创建了一个表单。用户将输入文本,它将传递到后端的服务器,并且将出现一个新文本。我可以通过更改 TextField 的值轻松做到这一点。我的问题是,当我在 TextField 中显示输出时,我希望突出显示一些字符串,如图所示。 之前 之后

我可以从后端获取索引和标记,但我没有找到突出显示部分 TextField 值的方法。我发现的所有内容都是使用 InputProps 设置整个值的样式。我已经尝试使用这个答案和react-highlight-words package,但我无法使用它们设置值,因为它将返回一个对象,并且显示的值将是 [object object] 而不是部分突出显示的字符串我也试过find-substring-in-text-and-change-style-programmatically 并使用TextField而不是Text,但我只想要一个TextField,而不是每个单词的不同TextField。我在我的代码中添加了基于这个答案所做的事情。

这是我的代码的简化版本

index.js

 import React from 'react';
 import ReactDOM from 'react-dom';
 import './index.css';
 import App from './App';
 import registerServiceWorker from './registerServiceWorker';

 ReactDOM.render(<App />, document.getElementById('root'));
 registerServiceWorker();`

应用程序.js

import React, { Component } from 'react';
import './App.css';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
import HomePage from './HomePage';
import 'bootstrap/dist/css/bootstrap.css';

class App extends Component {

  render() {
    return (
      <div className="App">
      <>
        <Router>
          <Switch>
            <Route path='/' exact component={HomePage} />
          </Switch>
        </Router>
      </>
    </div>
    );
  }
}

export default App;

主页.js

import React from 'react'
import { TextField, Button } from "@material-ui/core";

const HomePage = () => {
  
    const [value, setValue] = React.useState();
    const [indices, setIndices] = React.useState();
    const [substrings, setSubstrings] = React.useState();

    const handleChange = (event) => {
        setValue(event.target.value)
    }

    const handleChange2 = (event) => {
      // What to set?
    }

    const handleClear = () => {
        setValue("")
    }

    const handleSubmit = () => {

        let axios = require('axios')

        let data = { 
            "text": value
        }

        axios.post('http://127.0.0.1:8000/predict', data)
            .then(response => (setValue(response.data.value),
            setIndices(response.data.indices),
            setSubstrings(response.data.substrings)
            ))

    }

    return (

      <form noValidate autoComplete="off">

        {/*This is the block that I tried from this link https://stackoverflow.com/questions/57808195/react-native-find-substring-in-text-and-change-style-programmatically*/}
        {/*
        
        {['red','black','green'].map(item => (
            <TextField 
            InputProps={{
              style: {
                background: item,
              },
            }}
            value={item}
            onChange={handleChange2}
            />
        ))}

        */}
                  
        <TextField
          value={value}
          rowsMax={15}
          InputProps={{
            style: {
              fontWeight: 500,
            },
          }}
          variant="outlined"
          onChange={handleChange}
        />
        
        <Button onClick={handleClear}>Clear</Button>
        <Button onClick={handleSubmit}>Submit</Button>

      </form>
      
    );
};

export default HomePage;

有没有办法做到这一点?还是更简单的方法,例如将 HTML 代码传递给值?

更新: 我使用原生 contentEditable native 解决了这个问题

正如 Julien Ripet 所建议的那样。这是更新后的 HomePage.js 代码。

import React from 'react'
import {Button } from "@material-ui/core";

const HomePage = () => {

    // This handler will listen to the paste event. It will serves as "paste as plain text" to avoid pasting the style.
    const handlePaste = (event) => {
      // cancel paste
      event.preventDefault();
      // get text representation of clipboard
      var text = (event.originalEvent || event).clipboardData.getData('text/plain');
      // insert text manually
      document.execCommand("insertText", false, text);
    }

    // Set the textbox to empty when the clear button is clicked
    const handleClear = () => {
      var inputText = document.getElementById("textbox");
      inputText.innerHTML = "";
    }

    // Send the text to the server, pass the returned value with its indices to the highlight function to be highlighted and rendered on the same box
    const handleSubmit = () => {

      // Get the object of the textbox
      var inputText = document.getElementById("textbox");
      // use innerHTML instead of innerText if you want a string with the html tags
      var onlyText = inputText.innerText.trim().replaceAll("\n\n","\n")
      
      // Send data to the server, the response will be (value, indices). Pass it to the highlight function to be highlighted and rendered to the textbox.
      /*
      let axios = require('axios')
      let data = { 
        "text": onlyText
      }
      axios.post('http://127.0.0.1:8000/predict', data)
          .then(response => (highlight(response.data.value, response.data.indices)))          
      */

      // Example
      var text = "We are trying this text just to see the output";
      var indices = [{"start":1, "end":3},{"start":7, "end":8}];      
      highlight(text, indices)
    }

    const highlight = (text, indices) => {
      // Get the object of the textbox
      var inputText = document.getElementById("textbox");
  
      // Split the string to an array of strings for easier processing
      text = text.split(" ");
      
      // enclose to be highlighted words with an HTML span of class highlight 
      for (var i = 0; i < indices.length; i++) {
        text[indices[i].start] = "<span class='highlight'>" + text[indices[i].start] 
        text[indices[i].end -1] = text[indices[i].end -1] + "</span>"
      }
  
      // Join the array of strings into one string
      var new_text = text.join(' ')
      
      // Update the content of the textbox object with the highlighted text
      inputText.innerHTML = new_text;
    }
    
    
    return (

      <form noValidate autoComplete="off">

      <p
        className="textbox"
        variant="outlined" 
        id="textbox"
        onPaste={handlePaste}  /** Handle the paset event to avoid pasting the style*/
        fontWeight = {500}
        contentEditable = {true}
      ></p>

      <Button onClick={handleClear}>Clear</Button>
      <Button onClick={handleSubmit}>Submit</Button>

      </form>

    );
};

export default HomePage;

添加到 App.css

.highlight {
  background-color: yellow;
}

.textbox {
  border: 1px solid #000000;
}

这里有一些对我有帮助的stackoverflow问题:
get the text content from a contenteditable div through javascript
Javascript trick for "paste as plain text" in execCommand

标签: javascriptreactjs

解决方案


欢迎来到 StackOverflow。

无论如何,我看不到通过反应或任何相关软件包解决您的问题。我可以看到它完成的一种方法是将您的 TextField 替换为<p>具有属性的本机contenteditable="true",并根据需要设置样式,可能看起来尽可能接近原始 TextField。

请告诉我您是否设法通过我的替代解决方案或其他方式做到这一点


推荐阅读