使用以下两个api
需要 API
const Range = useCallback(() => {
const selObj = window.getSelection();
if (!selObj) return;
return selObj.getRangeAt(0);
}, []);
删除代码
const range = Range();
if (!range) return;
range.deleteContents();// 删除节点
加粗功能代码(修改 加入markdown 语法 ** ** 加粗)
onClick={() => { const range = Range(); if (!range) return; const t = document.createElement("span"); range.surroundContents(t);//标签先加入 然后在改值 t.innerHTML = `**${range.toString()}**`; }}
如果选中的文字不是同一行(不是同一个元素)需要先删除原来的 再添加新的
const range = Range(); if (!range) return; const prev = range.toString(); range.deleteContents(); const t = document.createElement("span"); range.surroundContents(t); t.innerHTML = `**${prev}**`;
完整代码 仅供参考
import { Button } from "antd";
import { useState, useCallback, useRef, useMemo } from "react";
import { FC, ReactElement } from "react";
import styled from "styled-components";
import MarkDown from "../components/MD/MarkDown";
interface IProps {}
const MD: FC<IProps> = (): ReactElement => {
const [content, setContent] = useState("");
const input = useRef<HTMLInputElement | null>(null);
const inputEvent = useMemo(() => {
const event = document.createEvent("HTMLEvents");
event.initEvent("input", true, true);
return event;
}, []);
const Range = useCallback(() => {
const selObj = window.getSelection();
if (!selObj) return;
return selObj.getRangeAt(0);
}, []);
const onInput = useCallback(e => {
setContent(e.target.innerText);
}, []);
const forceInput = useCallback(() => {
input.current?.dispatchEvent(inputEvent);
}, [inputEvent]);
return (
<Container>
<Nav>
<Button
onClick={() => {
const range = Range();
if (!range) return;
range.deleteContents();
forceInput();
}}
>
删除
</Button>
<Button
onClick={() => {
const range = Range();
if (!range) return;
const t = document.createElement("span");
range.surroundContents(t);
t.innerHTML = `**${range.toString()}**`;
forceInput();
range.collapse(false);
}}
>
加粗
</Button>
</Nav>
<EditWrapper>
<div ref={input} onInput={onInput} contentEditable='true'></div>
<MarkDown content={content} />
</EditWrapper>
</Container>
);
};
export default MD;
const Container = styled.div`
display: flex;
flex-direction: column;
width: 100vw;
height: 100vh;
`;
const Nav = styled.nav`
width: 100%;
height: 50px;
background-color: #000;
position: -webkit-sticky;
position: sticky;
top: 0;
z-index: 1;
`;
const EditWrapper = styled.div`
height: calc(100% - 50px);
width: 100%;
display: flex;
& > div {
width: 50%;
max-width: 50%;
padding: 0 5px;
}
& > div:first-child {
border-right: 1px dashed #ccc;
outline: none;
}
`;