1

i'm trying to build some text editor with react

i'm not native in english, so sorry about below question


what i want to do is like this below

(it's 'notion' page gif)

  • if user drag text with mouse(or with keyboard) to set a block in some texts,
  • popup menu(to edit 'seleted texts') show

i'm stuck at first one.

i can get whole text and selected text

  • let wholeText = whole.anchorNode.childNodes[0].defaultValue;
  • let selectedText = window.getSelection().toString();

but i can't get 'exact location' of selectedText in wholeText


here's my code (react, styled-component)

import { useState, useEffect } from 'react';
import styled from 'styled-components';
import autosize from 'autosize';

export default function TextBox({ id, index, content, handleContentInput }) {
  const [currentContent, setCurrentContent] = useState(content);
  const [areaHeight, setAreaHeight] = useState(25);

  useEffect(() => {
    autosize(document.querySelector(`.TextBoxWrap_${id}`));
  }, [currentContent]);

  return (
    <TextBoxContainer
      className={`TextBoxContainer_${id}`}
      areaHeight={areaHeight}
      onClick={() => {
        document.querySelector(`.TextBoxContainer_${id}`).focus();
      }}
    >
      <TextBoxWrap
        className={`TextBoxWrap_${id}`}
        areaHeight={areaHeight}
        onChange={(event) => {
          setCurrentContent(event.target.value);
        }}
        onBlur={() => handleContentInput(id, index, currentContent)}
        onKeyUp={() => {
          let newHeight = document.querySelector(`.TextBoxWrap_${id}`)
            .clientHeight;
          setAreaHeight(newHeight);
        }}
        onMouseUp={() => {
          let whole = window.getSelection();
          if (!whole) return;

          let range = whole.getRangeAt(0);

          console.log({ whole, range });

          let wholeText = whole.anchorNode.childNodes[0].defaultValue;

          let selectedText = whole.toString();

          console.log({ wholeText, selectedText });

          let start = range.startOffset; // Start position
          let end = range.endOffset; // End position

          console.log({ start, end });
        }}
        value={currentContent}
      ></TextBoxWrap>
    </TextBoxContainer>
  );
}

const TextBoxContainer = styled.div`
  max-width: 890px;
  width: 100%;
  height: ${(props) => `${props.areaHeight}px`};

  border: 1px solid black;
  margin: 3px;
  padding: 2px;
`;

const TextBoxWrap = styled.textarea`
  /* border: 1px solid black; */
  box-sizing: 'border-box';
  border: none;
  outline: none;

  width: 100%;
  min-height: 20px;
  height: 20px;

  /* padding: 2x; */
  box-shadow: none;
  display: block;
  overflow: hidden; // Removes scrollbar
  resize: none;
  font-size: 18px;
  /* line-height: 1.5em; */
  /* font-family: Georgia, 'Malgun Gothic', serif; */
`;

inside of onMouseUp, it works like this

  • console.log({ wholeText, selectedText }); : works fine
  • console.log({ start, end }); : always 0, 0

wholeText.indexOf(selectedText) was my option, but indexOf() gives me just 'first' index matches,

so, even if i want "aaaaaa'a'aa" but indexOf will give me '0' (=> "'a'aaaaaaaaaa")


what i was thinking,

  • get the 'exact index(location)', and cut wholeText with it
  • decorate selectedText with popup box (return html element? like <span style{{color: 'red'}}>${selectedText}
  • then, combine these three items (wholeText_front, decorated_selectedText, wholeText_back)
  • and, to place popup menu div right above seleted texts

which means, i need 'selected one's location


how can i do this? plz give me advice

thank you!


posts i read before post this question

Kim GyuHwa
  • 21
  • 5

1 Answers1

1

i might have some solution for my question, so post this answer myself


in this question(How to get the start and end points of selection in text area?), 'Tim Down' answered with getInputSelection function.

getInputSelection needs el as parameter, and i thought this is some kind of 'html element'

so, from this question(get id of focused element using javascript ), i got a hint : document.activeElement

and, i tried getInputSelection(document.activeElement) and got an object like { start: (number), end: (number) }

Kim GyuHwa
  • 21
  • 5