css 光标在contenteditable div中不移动

jogvjijk  于 2024-01-09  发布在  其他
关注(0)|答案(1)|浏览(127)

我试图使一个富文本编辑器的React。我卡住了,由于问题。问题是,与每一个字符,我键入的插入点是不与由于这光标被卡住的左边和字符被插入在左边。此外,当我手动移动光标在一个字的末尾,并尝试再次键入它移动到左边。这是我的texteditor的截图(https://i.stack.imgur.com/TnZNT.png

import React from "react";
import { useState } from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
  faBold,
  faCode,
  faFileCode,
  faFilm,
  faImage,
  faItalic,
  faLink,
  faList,
  faListOl,
  faQuoteRight,
  faStrikethrough,
  faUnderline,
} from "@fortawesome/free-solid-svg-icons";
import "./textEditor.css";

const TextEditor = () => {
  const [content, setContent] = useState("");
  const [isBold, setIsBold] = useState(false);
  const [isItalic, setIsItalic] = useState(false);
  const [isUnderline, setIsUnderline] = useState(false);
  const [isStrikeThrough, setStrikeThrough] = useState(false);
  const [isBlockquote, setIsBlockquote] = useState(false);
  const [isHeading1, setIsHeading1] = useState(false);
  const [isHeading2, setIsHeading2] = useState(false);
  const [isOrderedList, setIsOrderedList] = useState(false);
  const [isUnorderedList, setIsUnorderedList] = useState(false);
  const [isInlineCode, setIsInlineCode] = useState(false);
  const [isCodeBlock, setIsCodeBlock] = useState(false);

  const handleFormat = (style, value = null) => {
    // Toggle the state for the corresponding style
    switch (style) {
      case "bold":
        setIsBold(!isBold);
        break;
      case "italic":
        setIsItalic(!isItalic);
        break;
      case "underline":
        setIsUnderline(!isUnderline);
        break;
      case "strikeThrough":
        setStrikeThrough(!isStrikeThrough);
        break;
      case "formatBlock":
        // Handle specific cases for formatBlock
        if (value === "<blockquote>") {
          setIsBlockquote(!isBlockquote);
          value = isBlockquote ? "<p>" : "<blockquote>";
        } else if (value === "<h1>") {
          setIsHeading1(!isHeading1);
          value = isHeading1 ? "<p>" : "<h1>";
        } else if (value === "<h2>") {
          setIsHeading2(!isHeading2);
          value = isHeading2 ? "<p>" : "<h2>";
        } else if (value === "<pre>") {
          setIsCodeBlock(!isCodeBlock);
          value = isCodeBlock ? "<p>" : "<pre>";
        }
        break;
      case "insertOrderedList":
        setIsOrderedList(!isOrderedList);
        break;
      case "insertUnorderedList":
        setIsUnorderedList(!isUnorderedList);
        break;
      case "code":
        setIsInlineCode(!isInlineCode);
        break;

      case "createLink":
        const url = prompt("Enter the link URL:");
        document.execCommand("createLink", false, url);
        break;

      default:
        break;
    }

    // Check the current state before applying the style
    document.execCommand(style, false, value);

    
  };
  return (
    <div className="editor-wrapper">
      <div className="editor">
        <div className="toolbar">
          <button className="editor-btn" onClick={() => handleFormat("bold")}>
            <FontAwesomeIcon icon={faBold} />
          </button>

          <button
            className="editor-btn"
            onClick={() => handleFormat("underline")}
          >
            <FontAwesomeIcon icon={faUnderline} />
          </button>

          <button className="editor-btn" onClick={() => handleFormat("italic")}>
            <FontAwesomeIcon icon={faItalic} />
          </button>

          <button
            className="editor-btn"
            onClick={() => handleFormat("strikeThrough")}
          >
            <FontAwesomeIcon icon={faStrikethrough} />
          </button>

          <button
            className="editor-btn"
            onClick={() => handleFormat("formatBlock", "<blockquote>")}
          >
            <FontAwesomeIcon icon={faQuoteRight} />
          </button>

          <button
            className="editor-btn"
            style={{ fontWeight: "bold" }}
            onClick={() => handleFormat("formatBlock", "<h1>")}
          >
            H1
          </button>

          <button
            className="editor-btn"
            style={{ fontWeight: "bold" }}
            onClick={() => handleFormat("formatBlock", "<h2>")}
          >
            H2
          </button>

          <button
            className="editor-btn"
            onClick={() => handleFormat("insertUnorderedList")}
          >
            <FontAwesomeIcon icon={faList} />
          </button>

          <button
            className="editor-btn"
            onClick={() => handleFormat("insertOrderedList")}
          >
            <FontAwesomeIcon icon={faListOl} />
          </button>

          <button className="editor-btn" onClick={() => handleFormat("code")}>
            <FontAwesomeIcon icon={faCode} />
          </button>

          <button
            className="editor-btn"
            onClick={() => handleFormat("formatBlock", "<pre>")}
          >
            <FontAwesomeIcon icon={faFileCode} />
          </button>

          <button
            className="editor-btn"
            onClick={() => handleFormat("createLink")}
          >
            <FontAwesomeIcon icon={faLink} />
          </button>
          <button
            className="editor-btn"
            onClick={() =>
              handleFormat("insertImage", prompt("Enter image URL:"))
            }
          >
            <FontAwesomeIcon icon={faImage} />
          </button>

          <button
            className="editor-btn"
            onClick={() =>
              handleFormat(
                "insertHTML",
                '<iframe width="560" height="315" src="https://www.youtube.com/embed/VIDEO_ID" frameborder="0" allowfullscreen></iframe>'
              )
            }
          >
            <FontAwesomeIcon icon={faFilm} />
          </button>
        </div>
        <div
          className="text-editor"
          contentEditable="true"
          dir="ltr"
          dangerouslySetInnerHTML={{ __html: content }}
          onInput={(e) => {
            setContent(e.target.innerHTML);
            e.target.focus();
          }}
        />
        <br />
      </div>
    </div>
  );
};

export default TextEditor;

字符串
CSS

.editor-wrapper {
  width: 100%;
  height: 500px;
  display: flex;

  flex-direction: column;
  align-items: center;
}

.editor {
  width: 70%;
  
}

.toolbar {
  border: 1px solid black;
}
.text-editor {
  border: 1px solid black;
  padding-left: 2px;
  min-height: 100px;

  resize: vertical;
  overflow: auto;
}

.editor:focus {
  border: none;
}

.editor-btn {
  background: none;
  border: none;
  cursor: pointer;
  color: #414141;
}

.editor-btn:hover {
  color: blue;
}

.text-editor blockquote {
  border-left: 4px solid #414141;
  margin-right: 2px;
}

.text-editor pre {
  background-color: #dbdbdb;
  word-wrap: break-word;
}

.text-editor code {
  background-color: #dbdbdb;
}

7dl7o3gd

7dl7o3gd1#

我解决了这个问题,添加了一个函数来聚焦光标和用户,在内容钩子上有一个监听器,并像这样执行focus函数:

function focus() {
    const contentEle = document.getElementById('content');
    const range = document.createRange();
    const selection = window.getSelection();
    range.setStart(contentEle, contentEle.childNodes.length);
    range.collapse(true);
    selection.removeAllRanges();
    selection.addRange(range);
  }

  useEffect(() => {
    focus();
  }, [content]);

字符串
这是我创建的函数的代码。

import React from "react";
import { useState, useEffect } from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
  faBold,
  faCode,
  faFileCode,
  faFilm,
  faImage,
  faItalic,
  faLink,
  faList,
  faListOl,
  faQuoteRight,
  faStrikethrough,
  faUnderline,
} from "@fortawesome/free-solid-svg-icons";
import "./textEditor.css";

const TextEditor = () => {
  const [content, setContent] = useState("");
  const [isBold, setIsBold] = useState(false);
  const [isItalic, setIsItalic] = useState(false);
  const [isUnderline, setIsUnderline] = useState(false);
  const [isStrikeThrough, setStrikeThrough] = useState(false);
  const [isBlockquote, setIsBlockquote] = useState(false);
  const [isHeading1, setIsHeading1] = useState(false);
  const [isHeading2, setIsHeading2] = useState(false);
  const [isOrderedList, setIsOrderedList] = useState(false);
  const [isUnorderedList, setIsUnorderedList] = useState(false);
  const [isInlineCode, setIsInlineCode] = useState(false);
  const [isCodeBlock, setIsCodeBlock] = useState(false);

  const handleFormat = (style, value = null) => {
    // Toggle the state for the corresponding style
    switch (style) {
      case "bold":
        setIsBold(!isBold);
        break;
      case "italic":
        setIsItalic(!isItalic);
        break;
      case "underline":
        setIsUnderline(!isUnderline);
        break;
      case "strikeThrough":
        setStrikeThrough(!isStrikeThrough);
        break;
      case "formatBlock":
        // Handle specific cases for formatBlock
        if (value === "<blockquote>") {
          setIsBlockquote(!isBlockquote);
          value = isBlockquote ? "<p>" : "<blockquote>";
        } else if (value === "<h1>") {
          setIsHeading1(!isHeading1);
          value = isHeading1 ? "<p>" : "<h1>";
        } else if (value === "<h2>") {
          setIsHeading2(!isHeading2);
          value = isHeading2 ? "<p>" : "<h2>";
        } else if (value === "<pre>") {
          setIsCodeBlock(!isCodeBlock);
          value = isCodeBlock ? "<p>" : "<pre>";
        }
        break;
      case "insertOrderedList":
        setIsOrderedList(!isOrderedList);
        break;
      case "insertUnorderedList":
        setIsUnorderedList(!isUnorderedList);
        break;
      case "code":
        setIsInlineCode(!isInlineCode);
        break;

      case "createLink":
        const url = prompt("Enter the link URL:");
        document.execCommand("createLink", false, url);
        break;

      default:
        break;
    }

    // Check the current state before applying the style
    document.execCommand(style, false, value);


  };

  function focus() {
    const contentEle = document.getElementById('content');
    const range = document.createRange();
    const selection = window.getSelection();
    range.setStart(contentEle, contentEle.childNodes.length);
    range.collapse(true);
    selection.removeAllRanges();
    selection.addRange(range);
  }

  useEffect(() => {
    focus();
  }, [content]);

  return (
    <div className="editor-wrapper">
      <div className="editor">
        <div className="toolbar">
          <button className="editor-btn" onClick={() => handleFormat("bold")}>
            <FontAwesomeIcon icon={faBold} />
          </button>

          <button
            className="editor-btn"
            onClick={() => handleFormat("underline")}
          >
            <FontAwesomeIcon icon={faUnderline} />
          </button>

          <button className="editor-btn" onClick={() => handleFormat("italic")}>
            <FontAwesomeIcon icon={faItalic} />
          </button>

          <button
            className="editor-btn"
            onClick={() => handleFormat("strikeThrough")}
          >
            <FontAwesomeIcon icon={faStrikethrough} />
          </button>

          <button
            className="editor-btn"
            onClick={() => handleFormat("formatBlock", "<blockquote>")}
          >
            <FontAwesomeIcon icon={faQuoteRight} />
          </button>

          <button
            className="editor-btn"
            style={{ fontWeight: "bold" }}
            onClick={() => handleFormat("formatBlock", "<h1>")}
          >
            H1
          </button>

          <button
            className="editor-btn"
            style={{ fontWeight: "bold" }}
            onClick={() => handleFormat("formatBlock", "<h2>")}
          >
            H2
          </button>

          <button
            className="editor-btn"
            onClick={() => handleFormat("insertUnorderedList")}
          >
            <FontAwesomeIcon icon={faList} />
          </button>

          <button
            className="editor-btn"
            onClick={() => handleFormat("insertOrderedList")}
          >
            <FontAwesomeIcon icon={faListOl} />
          </button>

          <button className="editor-btn" onClick={() => handleFormat("code")}>
            <FontAwesomeIcon icon={faCode} />
          </button>

          <button
            className="editor-btn"
            onClick={() => handleFormat("formatBlock", "<pre>")}
          >
            <FontAwesomeIcon icon={faFileCode} />
          </button>

          <button
            className="editor-btn"
            onClick={() => handleFormat("createLink")}
          >
            <FontAwesomeIcon icon={faLink} />
          </button>
          <button
            className="editor-btn"
            onClick={() =>
              handleFormat("insertImage", prompt("Enter image URL:"))
            }
          >
            <FontAwesomeIcon icon={faImage} />
          </button>

          <button
            className="editor-btn"
            onClick={() =>
              handleFormat(
                "insertHTML",
                '<iframe width="560" height="315" src="https://www.youtube.com/embed/VIDEO_ID" frameborder="0" allowfullscreen></iframe>'
              )
            }
          >
            <FontAwesomeIcon icon={faFilm} />
          </button>
        </div>
        <div
          id="content"
          className="text-editor"
          contentEditable="true"
          dir="ltr"
          dangerouslySetInnerHTML={{ __html: content }}
          onInput={(e) => {
            setContent(e.target.innerHTML);
            e.target.focus();
          }}

        />
        <br />
      </div>

      <button onClick={focus}>test123</button>

    </div>
  );
};

export default TextEditor;


这里有一个react项目的链接,你的问题已经解决了https://github.com/pablosalazzar/stackoverflow-answer-2

相关问题