Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

199
Vistas
element.focus() doesn't work on rerendered contentEditable div element

Edit little-platform-3291w

import React, { useEffect, useState } from "react";

import "./styles.css";

const RichText = () => {
  const [value, setValue] = useState("");

  useEffect(() => {
    const div = document.getElementById("textarea");

    if (div) {
      setTimeout(() => {
        div.focus();
      }, 0);
    }
  });

  return (
    <div
      className="rich-text"
      onInput={(e) => {
        setValue(e.target.innerText);
      }}
      contentEditable
      id="textarea"
      dangerouslySetInnerHTML={{
        __html: value
      }}
    />
  );
};

export default RichText;

I want to implement rich text component, the idea , is that you can type text inside some field , you can style this text ( make it bold, italic, underlined , etc) . I want to same text value inside on value state variable , and then wrap it somehow inside of html tags <p>Hello <b> Andrew</b></p> , and show it in real time inside the same field , styled. For showing html tags inside of div , contentEditable field, I need to use dangerouslySetInnerHTML , and this is the main problem. That on each button press , I update value, component then rerendered , focus goes at the start of field , but I want it to be at the end in time you typing new text . I've tried to make it by ref => ref.current.focus() , it doesn't work, in code above you can see that I also try to make it by vanilla js with using timeout , it also doesn't work , autoFocus - can be used only on input, textarea, etc , div can be used with this property . I have save it to ref , but then I can't show wrapped html inside of div . Tried a lot of cases , but it is seemles . Any ideas how to make it ?

about 4 years ago · Juan Pablo Isaza
2 Respuestas
Responde la pregunta

0

The issue is when using useState hook with contentEditable and dangerouslySetInnerHTML to sync value. When you type something in the div it rerenders again and brings the cursor back to the start.

You can use instance variables in the function component (useRef to update value) to get rid of the issue.

And you should use innerHTML instead of innerText to get the HTML string saved

Try like below

import React, { useRef } from "react";

import "./styles.css";

const RichText = () => {
  const editableRef = useRef(null);
  const { current: value } = useRef(
    '<div style="color:green">my initial content</div>'
  );

  const setValue = (e) => {
    value.current = e.target.innerHTML;
  };

  const keepFocus = () => {
    const { current } = editableRef;
    if (current) {
      current.focus();
    }
  };

  return (
    <div
      className="rich-text"
      onInput={setValue}
      contentEditable
      id="textarea"
      ref={editableRef}
      onBlur={keepFocus}
      dangerouslySetInnerHTML={{
        __html: value
      }}
    />
  );
};

export default RichText;

Code Sandbox

about 4 years ago · Juan Pablo Isaza Denunciar

0

import React from "react";

import "./styles.css";

class TextInput extends React.Component {
  constructor(props) {
    super();
    this.state = { value: "" };
  }

  shouldComponentUpdate() {
    return true;
  }

  componentDidUpdate() {
    const el = document.getElementById("textarea");

    if (el) {
      console.log(el.selectionStart, el.selectionEnd);

      var range, selection;
      if (document.createRange) {
        //Firefox, Chrome, Opera, Safari, IE 9+
        range = document.createRange(); //Create a range (a range is a like the selection but invisible)
        range.selectNodeContents(el); //Select the entire contents of the element with the range
        range.collapse(false); //collapse the range to the end point. false means collapse to end rather than the start
        selection = window.getSelection(); //get the selection object (allows you to change selection)
        selection.removeAllRanges(); //remove any selections already made
        selection.addRange(range); //make the range you have just created the visible selection
      } else if (document.selection) {
        //IE 8 and lower
        range = document.body.createTextRange(); //Create a range (a range is a like the selection but invisible)
        range.moveToElementText(el); //Select the entire contents of the element with the range
        range.collapse(false); //collapse the range to the end point. false means collapse to end rather than the start
        range.select(); //Select the range (make it the visible selection
      }
    }
  }

  update(value) {
    this.setState({ value });
  }

  render() {
    console.log(this.state.value);
    return (
      <div
        className="rich-text"
        onInput={(e) => {
          this.update(e.target.innerText);
        }}
        contentEditable
        id="textarea"
        dangerouslySetInnerHTML={{
          __html: `<b>${this.state.value}</b>`
        }}
      />
    );
  }
}

export default TextInput;
about 4 years ago · Juan Pablo Isaza Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda