Сегодня на многих веб-сайтах есть разные места, где пользователям разрешено выражать свои мысли. Это могут быть комментарии к записи в блоге или статье, отзывы об услугах компании и т. Д.
Веб-сайты для создания контента и некоторые платформы обмена сообщениями позволяют пользователям добавлять к своим сообщениям базовое форматирование текста, например выделять слова жирным шрифтом, подчеркивать и т. Д. В этой статье я опишу, как добавить некоторые простые параметры форматирования текста на веб-страницу, созданную с помощью React.js.
Подготовка сцены
Начнем с создания простой страницы с текстовой областью и тремя кнопками - полужирным шрифтом, курсивом и подчеркиванием:
import React from 'react'; import './App.css'; class App extends React.Component { constructor() { super(); } render() { return ( <div className="App"> <header className="App-header"> <span className="Controls"> <button><strong>B</strong></button> <button><em>I</em></button> <button><u>U</u></button> </span> <textarea rows="5" className="Text" /> </header> </div> ); } } export default App;
Затем мы добавляем стили:
.App { text-align: center; } .App-header { min-height: 100vh; display: flex; flex-direction: column; align-items: center; justify-content: center; font-size: calc(10px + 2vmin); } .Controls { border: 1px solid grey; width: 50vw; text-align: left; padding: 10px; } button { font-size: calc(8px + 2vmin); margin: 0 2px; width: 50px; background-color: white; cursor: pointer; padding: 2px; } .Text { font-size: calc(5px + 2vmin); width: 50vw; padding: 10px; }
Приведенный выше код дает нам следующее:
Затем мы добавляем обработчики событий к кнопкам, чтобы при каждом нажатии на одну из них цвет фона менялся на светло-серый:
/* CSS */ .Selected { background-color: lightgrey; color: white; } ... // JS class App extends React.Component { constructor() { super(); this.onBoldClick = this.onBoldClick.bind(this); this.onItalicsClick = this.onItalicsClick.bind(this); this.onUnderlineClick = this.onUnderlineClick.bind(this); } onBoldClick(event) { event.target.setAttribute("class", !this.state.bold ? "Selected" : ""); } onItalicsClick(event) { event.target.setAttribute("class", !this.state.italized ? "Selected" : ""); } onUnderlineClick(event) { event.target.setAttribute("class", !this.state.underlined ? "Selected" : ""); } render() { ... <button onClick={this.onBoldClick}><strong>B</strong></button> <button onClick={this.onItalicsClick}><em>I</em></button> <button onClick={this.onUnderlineClick}><u>U</u></button> ... } }
Приведенный выше код зависит от трех параметров состояния: this.state.bold
, this.state.italized
andis.state.underlined
, которые сообщают нам, какой из параметров форматирования выбран в данный момент. Нам нужно добавить их в конструктор:
constructor() { ... this.state = { bold: false, italized: false, underlined: false }; }
При щелчке полужирным шрифтом (B) приведенное выше дает нам следующий результат:
Добавление логики
Что мы хотим сделать, так это отформатировать любой текст, который написан в текстовой области, и отобразить результат. Для этого мы создадим тег div для хранения результата, а затем присвоим каждому тегу вывода и текстовой области ref, чтобы мы могли легко получить к ним доступ позже:
constructor() { super(); this.inputRef = React.createRef(); this.outputRef = React.createRef(); ... } ... <header className="App-header"> <div ref={this.outputRef}></div> <span className="Controls"> <button onClick={this.onBoldClick}><strong>B</strong></button> <button onClick={this.onItalicsClick}><em>I</em></button> <button onClick={this.onUnderlineClick}><u>U</u></button> </span> <textarea rows="5" className="Text" ref={this.inputRef} /> </header> ...
Затем мы добавляем метод, который будет вызываться всякий раз, когда текст в текстовой области изменился:
onInputChange() { // Do something } ... render() { ... <textarea rows="5" className="Text" ref={this.inputRef} onChange={this.onInputChange} /> ... }
Затем мы обновим методы обработки событий щелчка для трех кнопок:
onBoldClick(event) { event.target.setAttribute("class", !this.state.bold ? "Selected" : ""); if (!this.state.bold) { this.outputRef.current.innerHTML += "<strong></strong>"; } this.setState({ bold: !this.state.bold }); this.inputRef.current.focus(); }
Вот что делает приведенный выше код: если нажата кнопка, выделенная жирным шрифтом, она меняет цвет фона, как и раньше, а затем проверяет, становится ли это событие щелчка полужирным или нет. Если его включить, то он добавляет <strong></strong>
к выходному div. Этот сильный тег будет содержать любой текст, который пользователь вводит следующим, и сделает его жирным. Наконец, он изменяет состояние полужирного флага (на false, если он был истинным, или на true, если он был ложным). Эта логика повторяется для кнопок, выделенных курсивом и подчеркиванием.
Следующим шагом является добавление метода с именем formatText (), который будет принимать строку в качестве своего атрибута, проверять, какой из параметров форматирования был выбран, и соответственно применять форматирование. Если параметр форматирования не выбран, он просто добавляет строку к выводу:
formatText(text) { switch (true) { case this.state.bold: const allBold = this.outputRef.current.getElementsByTagName("strong"); const lastBold = allBold[allBold.length - 1]; lastBold.innerText += text; break; case this.state.italized: const allItalized = this.outputRef.current.getElementsByTagName("em"); const lastItalized = allItalized[allItalized.length - 1]; lastItalized.innerText += text; break; case this.state.underlined: const allUnderlined = this.outputRef.current.getElementsByTagName("u"); const lastUnderlined = allUnderlined[allUnderlined.length - 1]; lastUnderlined.innerText += text; break; default: this.outputRef.current.innerHTML += text; break; } }
Метод formatText () работает, проверяя, какой из параметров форматирования является истинным (или выбранным). Если выбран полужирный шрифт, выполняется проверка последнего элемента strong в выходном div и добавляется текст к элементу. Это сделает текст жирным. То же самое сделано для вариантов курсива и подчеркивания.
Мы будем вызывать этот метод formatText () каждый раз, когда текст в текстовой области изменяется:
onInputChange() { const input = this.inputRef.current.value; const output = this.outputRef.current.innerText; const newText = input.slice(output.length); this.formatText(newText); }
В приведенном выше примере, когда пользователь вводит что-то в текстовую область, текст форматируется, если выбран параметр форматирования, в противном случае он добавляется в выходной div:
Однако есть проблема - если пользователь совершает ошибку и удаляет символ, изменение не отражается в выводе, то есть вывод обновляется только тогда, когда пользователь вводит что-то новое, а не при удалении текста:
Чтобы решить эту проблему, мы добавим новый метод для обработки, когда длина текста в текстовой области меньше или равна длине текста в выводе. Это может произойти, если пользователь удалит или заменит часть текста.
transferText() { const input = this.inputRef.current.value; const output = this.outputRef.current.innerHTML; let inputCounter = input.length - 1, outputCounter = output.length - 1, isTag = false; while (outputCounter > -1) { // If the current character is '>', then we are in a HTML tag. Skip until we get to '<'. if (output[outputCounter] === ">") { isTag = true; outputCounter -= 1; continue; } if (isTag) { isTag = output[outputCounter] !== "<"; outputCounter -= 1; continue; } // If inputCounter <= -1, then there is no more text to add to the output, so break. if (inputCounter <= -1) { this.outputRef.current.innerHTML = this.outputRef.current.innerHTML.slice(outputCounter + 1); break; } // Otherwise, replace the text in the output with the corresponding text in the text area. else { let temp = this.outputRef.current.innerHTML; temp = temp.slice(0, outputCounter) + input[inputCounter] + temp.slice(outputCounter + 1); this.outputRef.current.innerHTML = temp; inputCounter -= 1; outputCounter -= 1; } } }
Вышеупомянутый метод transferText () просматривает текст во внутреннем HTML-коде выходного div, начиная справа, пропускает HTML-теги (такие как <strong>
, </em>
и т. Д.) И для каждого символа в выходном div, он заменяет текст в выводе соответствующим текстом в текстовой области. Если в текстовой области больше нет текста, а в выходном div все еще есть текст для цикла (это может произойти, если пользователь удалил текст), он очищает оставшийся текст в выходном div.
Затем мы добавим этот метод в обработчик события изменения для текстовой области:
onInputChange() { const input = this.inputRef.current.value; const output = this.outputRef.current.innerText; if (input.length > output.length) { const newText = input.slice(output.length); this.formatText(newText); } else { this.transferText(); } }
Обработчик событий теперь проверяет, больше ли текста в текстовой области, чем в выводе (что означает, что пользователь только что добавил текст), и форматирует текст, если это так. Если длина текста меньше или равна длине вывода, он вызывает метод transferText () для передачи текста из текстовой области в вывод.
Теперь удаление текста работает правильно:
Давайте протестируем форматирование текста с каждым из трех выбранных вариантов:
Заключение
Мы успешно создали веб-страницу с React, которая позволяет форматировать вводимый пользователем текст. Веб-страница поддерживает форматирование полужирным шрифтом, курсивом и подчеркиванием, и пользователи без проблем удаляют или изменяют набранный текст. Одним из ограничений вышеизложенного является то, что он не поддерживает комбинирование параметров форматирования (например, использование полужирного шрифта и подчеркивания для одного и того же текста).
Спасибо, что зашли так далеко. Вы можете найти полный код этого проекта здесь. Если у вас есть вопросы или комментарии, оставьте их ниже.