当前位置: 首页 > wzjs >正文

中山市网站建设上海十大装修公司

中山市网站建设,上海十大装修公司,境外服务器,旅游网站开发选题背景基于react框架,实现效果如下:目前只实现了标签添加,后续可能会用作输入模板填入,包含下拉、高亮等。实现原理:HTML的标签拥有 contenteditable 字段,将DOM交给浏览器,进行可编辑操作。纯HTML/JS…

基于react框架,实现效果如下:

目前只实现了标签添加,后续可能会用作输入模板填入,包含下拉、高亮等。

实现原理:HTML的标签拥有 contenteditable 字段,将DOM交给浏览器,进行可编辑操作。

纯HTML/JS的布局结构可以如下:

<div id="editor" contenteditable="true"><span contenteditable="false" class="tag">标签1</span><span contenteditable="true" id="inputArea"></span>
</div>

但React框架这样写会报错:

原因:

故,只能动态添加子元素:

    // 创建 用户输入区域 元素const createInputAreaElement = () => {const inputArea = document.createElement('span');inputArea.contentEditable = 'true';// 给一个空格占位符inputArea.innerHTML = defaultValue || '&nbsp;';inputArea.className = editorCss.userInputArea;return inputArea;}

输入区域需给一个空格占位符,避免span没宽度 

    // 创建输入区域useEffect(() => {const editor = contentRef.current;if (!editor) return;const userInput = createInputAreaElement();userInputRef.current = userInput;   // 记录用户输入区域// 初始添加提示和输入区域const placeholderArea = createPlaceholderElement();placeholderRef.current = placeholderArea;userInput.appendChild(placeholderArea)editor.appendChild(userInput);// 输入区域事件监听userInput.addEventListener('focus', handleFocus)userInput.addEventListener('blur', handleBlur)userInput.addEventListener('input', handleInput)}, []);
    return (<divref={contentRef}contentEditable={false}></div>);

主要步骤:

1. 组件初始化时,创建输入区域元素,将其添加到 contentRef 中。

2. 此处的输入区域做了 提示内容,将提示区域作为了输入区域的子元素,故需区分用户输入与提示内容

// 1. 提示区域设置data
placeholder.dataset.placeholder = 'true';// 2. 获取用户输入内容function getUserInputText() {const userInput = userInputRef.current;if (!userInput) return '';let text = '';userInput.childNodes.forEach(node => {if (node.nodeType === Node.TEXT_NODE) {text += node.textContent;} else if (node.nodeType === Node.ELEMENT_NODE) {const el = node as HTMLElement;if (!el.hasAttribute('data-placeholder')) {text += el.textContent;}}});return text.trim();}

3. 为输入区域添加事件监听:focus、blur、input,修改提示区域的显隐状态

4. 对外暴露添加标签操作,将标签插入到输入区域之前(也可选择插入到光标处)

  // 插入标签const insertTag = useCallback((tag: string) => {const editor = contentRef.current;const inputArea = userInputRef.current;const placeholder = placeholderRef.current;if (!editor || !inputArea) {console.log("容器未准备好!editor: ", editor, "input:", inputArea)return};// 创建新标签元素const newTag = createTagElement(tag);// 操作会导致失焦editor.insertBefore(newTag, inputArea)if (placeholder) {placeholder.style.display = 'none';}// ✅ 延迟聚焦,避免事件冲突setTimeout(() => {inputArea.focus();}, 0);}, []);

完整布局代码:不包含样式文件 

import React, {useState, useRef, useCallback, forwardRef, useImperativeHandle, useEffect} from 'react';
import editorCss from './editor.module.css';export interface EditHandler {insertTag: (tag: string) => void;setValue: (newValue: string) => void;
}interface EditProps {defaultValue?: string;onChange?: (html: string) => void;deleteTag?: (tag: string) => void;placeholderText?: string;
}const EditableWithTags = forwardRef<EditHandler, EditProps>((props, ref) => {const { defaultValue, onChange, deleteTag, placeholderText } = propsconst contentRef = useRef<HTMLDivElement>(null);const placeholderRef = useRef<HTMLSpanElement>(null);const userInputRef = useRef<HTMLSpanElement>(null);const isFocusedRef = useRef(false);// 暴露方法给外部useImperativeHandle(ref, () => ({insertTag: (tag: string) => {insertTag(tag);},setValue: (newValue: string) => {userInputRef.current.innerHTML = newValue;}}));// 创建 svg - 删除图标const svgDelete = () => {const svgNS = 'http://www.w3.org/2000/svg';const svg = document.createElementNS(svgNS, 'svg');svg.setAttribute('width', '12');svg.setAttribute('height', '12');svg.setAttribute('viewBox', '0 0 16 16');svg.setAttribute('fill', 'none');const path = document.createElementNS(svgNS, 'path');path.setAttribute('d','M9.625 1.08743C10.5677 1.63172 11.3518 2.41276 11.8999 3.35325C12.4479 4.29373 12.7409 5.36104 12.7496 6.44952C12.7584 7.538 12.4828 8.6099 11.95 9.55912C11.4172 10.5083 10.6458 11.3019 9.71203 11.8614C8.77828 12.4208 7.71461 12.7267 6.62631 12.7487C5.53802 12.7707 4.46284 12.5082 3.50722 11.987C2.5516 11.4658 1.74867 10.704 1.17792 9.77713C0.607164 8.85025 0.288381 7.79038 0.253125 6.70243L0.25 6.49993L0.253125 6.29743C0.288127 5.21805 0.602217 4.16616 1.16477 3.2443C1.72733 2.32245 2.51916 1.56209 3.46306 1.03736C4.40696 0.512628 5.47072 0.241431 6.55063 0.250206C7.63054 0.258982 8.68976 0.547431 9.625 1.08743ZM5.56938 4.71243C5.43843 4.63448 5.28351 4.60706 5.13376 4.63533C4.98401 4.66359 4.84974 4.7456 4.75622 4.86592C4.66269 4.98624 4.61635 5.13659 4.62591 5.28868C4.63547 5.44078 4.70027 5.58414 4.80813 5.69181L5.61563 6.49993L4.80813 7.30806L4.75625 7.36681C4.65912 7.49243 4.61344 7.65031 4.6285 7.80838C4.64356 7.96646 4.71822 8.11288 4.83733 8.2179C4.95643 8.32292 5.11104 8.37867 5.26976 8.37382C5.42848 8.36898 5.5794 8.3039 5.69188 8.19181L6.5 7.38431L7.30813 8.19181L7.36688 8.24368C7.4925 8.34081 7.65038 8.38649 7.80845 8.37143C7.96653 8.35637 8.11295 8.28171 8.21797 8.16261C8.32299 8.0435 8.37874 7.88889 8.37389 7.73017C8.36905 7.57145 8.30397 7.42053 8.19188 7.30806L7.38438 6.49993L8.19188 5.69181L8.24375 5.63306C8.34088 5.50744 8.38656 5.34956 8.3715 5.19148C8.35644 5.0334 8.28178 4.88698 8.16268 4.78196C8.04357 4.67694 7.88896 4.62119 7.73024 4.62604C7.57152 4.63089 7.4206 4.69596 7.30813 4.80806L6.5 5.61556L5.69188 4.80806L5.63313 4.75618L5.56938 4.71243Z');path.setAttribute('fill', '#666');svg.appendChild(path);return svg;};// 创建svg - 提示图标function svgStar() {// 创建 <svg> 元素const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");svg.setAttribute("width", "1rem");svg.setAttribute("height", "1rem");svg.setAttribute("viewBox", "0 0 18 18");svg.setAttribute("fill", "none");// 创建 <g> 元素const g = document.createElementNS("http://www.w3.org/2000/svg", "g");g.setAttribute("clip-path", "url(#clip0_274_2837)");// 创建 <path> 元素const path = document.createElementNS("http://www.w3.org/2000/svg", "path");path.setAttribute("d",`M12.6667 14.4998C13.1529 14.4998 13.6192 14.693 13.963 15.0368C14.3068 15.3806 14.5 15.8469 14.5 16.3332C14.5 15.8469 14.6932 15.3806 15.037 15.0368C15.3808 14.693 15.8471 14.4998 16.3333 14.4998C15.8471 14.4998 15.3808 14.3067 15.037 13.9629C14.6932 13.619 14.5 13.1527 14.5 12.6665C14.5 13.1527 14.3068 13.619 13.963 13.9629C13.6192 14.3067 13.1529 14.4998 12.6667 14.4998ZM12.6667 3.49984C13.1529 3.49984 13.6192 3.69299 13.963 4.03681C14.3068 4.38062 14.5 4.84694 14.5 5.33317C14.5 4.84694 14.6932 4.38062 15.037 4.03681C15.3808 3.69299 15.8471 3.49984 16.3333 3.49984C15.8471 3.49984 15.3808 3.30668 15.037 2.96287C14.6932 2.61905 14.5 2.15273 14.5 1.6665C14.5 2.15273 14.3068 2.61905 13.963 2.96287C13.6192 3.30668 13.1529 3.49984 12.6667 3.49984ZM6.25 14.4998C6.25 13.0411 6.82946 11.6422 7.86091 10.6107C8.89236 9.5793 10.2913 8.99984 11.75 8.99984C10.2913 8.99984 8.89236 8.42037 7.86091 7.38892C6.82946 6.35747 6.25 4.95853 6.25 3.49984C6.25 4.95853 5.67054 6.35747 4.63909 7.38892C3.60764 8.42037 2.20869 8.99984 0.75 8.99984C2.20869 8.99984 3.60764 9.5793 4.63909 10.6107C5.67054 11.6422 6.25 13.0411 6.25 14.4998Z`);path.setAttribute("stroke", "#878787");path.setAttribute("stroke-width", "1.5");path.setAttribute("stroke-linecap", "round");path.setAttribute("stroke-linejoin", "round");g.appendChild(path);// 创建 <defs> 和 <clipPath>const defs = document.createElementNS("http://www.w3.org/2000/svg", "defs");const clipPath = document.createElementNS("http://www.w3.org/2000/svg", "clipPath");clipPath.setAttribute("id", "clip0_274_2837");const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");rect.setAttribute("width", "22");rect.setAttribute("height", "22");rect.setAttribute("fill", "white");rect.setAttribute("transform", "translate(0 0.5)");clipPath.appendChild(rect);defs.appendChild(clipPath);// 组装 SVGsvg.appendChild(g);svg.appendChild(defs);return svg;}// 创建 tag 元素const createTagElement = (tag: string) => {const newTag = document.createElement('span');newTag.contentEditable = 'false';newTag.classList.add(editorCss.newTag)// 创建文本元素const text = document.createElement('span');text.innerText = `[${tag}]`;text.contentEditable = 'false';text.classList.add(editorCss.editableTag);const deleteBtn = document.createElement('span');deleteBtn.className = editorCss.deleteButton;const svg = svgDelete();deleteBtn.appendChild(svg);// 删除事件绑定deleteBtn.addEventListener('click', (e) => {e.preventDefault();newTag.remove();deleteTag(tag)});text.appendChild(deleteBtn);newTag.appendChild(text)return newTag;};// 创建 提示 元素const createPlaceholderElement = () => {const placeholder = document.createElement('span');placeholder.className = editorCss.inputAreaPlaceholder;placeholder.dataset.placeholder = 'true';  // 用于区分提示文本与用户输入const svg = svgStar();// placeholder.appendChild(svg);const text = document.createElement('span');text.innerText = placeholderText || '请输入内容';// text.className = editorCss.inputAreaPlaceholder;placeholder.appendChild(text);return placeholder;}// 创建 用户输入区域 元素const createInputAreaElement = () => {const inputArea = document.createElement('span');inputArea.contentEditable = 'true';// 给一个空格占位符inputArea.innerHTML = defaultValue || '&nbsp;';inputArea.className = editorCss.userInputArea;return inputArea;}/** 输入区域聚焦 */const handleFocus = () => {isFocusedRef.current = trueconst placeholder = placeholderRef.current;if (placeholder) {placeholder.style.display = 'none';}const userInput = userInputRef.current;if (userInput) {userInput.focus()}}/** 输入区域失焦 */const handleBlur = () => {isFocusedRef.current = falseconst placeholder = placeholderRef.current;if (getUserInputText() === '') {placeholder.style.display = 'inline-flex';}}/** 输入监听 */const handleInput = () => {onChange(getUserInputText())const isFocused = isFocusedRef.current;if (userInputRef.current.textContent.trim() !== '' || isFocused) {placeholderRef.current.style.display = 'none';} else {placeholderRef.current.style.display = 'inline-flex';}}/** 获取用户输入文本 */function getUserInputText() {const userInput = userInputRef.current;if (!userInput) return '';let text = '';userInput.childNodes.forEach(node => {if (node.nodeType === Node.TEXT_NODE) {text += node.textContent;} else if (node.nodeType === Node.ELEMENT_NODE) {const el = node as HTMLElement;if (!el.hasAttribute('data-placeholder')) {text += el.textContent;}}});return text.trim();}// 创建输入区域useEffect(() => {const editor = contentRef.current;if (!editor) return;const userInput = createInputAreaElement();userInputRef.current = userInput;   // 记录用户输入区域// 初始添加提示和输入区域const placeholderArea = createPlaceholderElement();placeholderRef.current = placeholderArea;userInput.appendChild(placeholderArea)editor.appendChild(userInput);// 输入区域事件监听userInput.addEventListener('focus', handleFocus)userInput.addEventListener('blur', handleBlur)userInput.addEventListener('input', handleInput)}, []);// 插入标签const insertTag = useCallback((tag: string) => {const editor = contentRef.current;const inputArea = userInputRef.current;const placeholder = placeholderRef.current;if (!editor || !inputArea) {console.log("容器未准备好!editor: ", editor, "input:", inputArea)return};// 创建新标签元素const newTag = createTagElement(tag);// 操作会导致失焦editor.insertBefore(newTag, inputArea)if (placeholder) {placeholder.style.display = 'none';}// ✅ 延迟聚焦,避免事件冲突setTimeout(() => {inputArea.focus();}, 0);}, []);return (<divref={contentRef}contentEditable={false}></div>);
});export default EditableWithTags;


文章转载自:

http://PM6wURrQ.wqbfd.cn
http://rfV5ejxB.wqbfd.cn
http://9A1ozG4Z.wqbfd.cn
http://U1MzujYp.wqbfd.cn
http://Ue3w1wtl.wqbfd.cn
http://OJDhqnfZ.wqbfd.cn
http://7RAaIUfh.wqbfd.cn
http://z5tlPT7r.wqbfd.cn
http://QtsVbqv3.wqbfd.cn
http://IREULDMn.wqbfd.cn
http://PihoAqfI.wqbfd.cn
http://cRhRuAeN.wqbfd.cn
http://gG01IW2b.wqbfd.cn
http://BHM2RriO.wqbfd.cn
http://GJLyCaKG.wqbfd.cn
http://VjHI40gz.wqbfd.cn
http://DHP6R4Zk.wqbfd.cn
http://MClFOuIP.wqbfd.cn
http://THvs2Lxb.wqbfd.cn
http://M1lMUrKS.wqbfd.cn
http://PA1E8qDB.wqbfd.cn
http://tdNvunIl.wqbfd.cn
http://jkiVdizg.wqbfd.cn
http://ICLJvL0m.wqbfd.cn
http://VuI9vgxK.wqbfd.cn
http://uFtyroZX.wqbfd.cn
http://ZkzBUt3m.wqbfd.cn
http://anv1dVpq.wqbfd.cn
http://GXuFIyKA.wqbfd.cn
http://AixyisHE.wqbfd.cn
http://www.dtcms.com/wzjs/762703.html

相关文章:

  • 如果自己建立网站专门做ppt的网站斧头
  • 国外装饰公司网站站长之家关键词查询
  • 免费做简单网站商城网站需要多少空间
  • rp做网站原型要缩小尺寸吗做网站和推广需要多少钱
  • 5星做号宿水软件的网站深圳医疗网站建设
  • 网站降权不更新文章可以吗针对人群不同 网站做细分
  • 购买已备案网站做非法免费网站建设 百度收录
  • 福州房产网站建设英文书 影印版 网站开发
  • 中国建筑网官方网站入口钱包钱夹移动网站建设
  • 中国建设银行网站会员登录可以做申论的网站
  • 杭州英文网站建设wordpress全站开启ssl
  • 新媒体网站建设方案深圳外贸业务员工资
  • 洛阳有做网站开发的吗网站建设柒金手指排名二一
  • 建设网站公司是什么网站做seo 反应非常慢
  • 阳泉营销型网站建设费用游戏如何在网上推广
  • 西宁做网站好的公司海阔天空网站建设
  • 教做年糕博客网站黑客入侵别人网站做seo
  • 做网站需要用什么系统百度seo外链推广教程
  • 精品课程网站建设开题报告建设三类人员报考网站
  • 哪家公司做网站开发做得比较好太原再次发出通告
  • 网站源码采集国家商标注册官网查询系统
  • 网站审批号开发一套app要多少钱
  • 做网站颜色黑色代码多少钱国外服务器免费ip地址
  • 中国建设银行钓鱼网站易动力建设网站怎么样
  • 中国建设银行官网站保本理财设计logo的ai软件
  • 黑龙江做网站的公司做亚马逊网站需要租办公室吗
  • 想用自己电脑做服务器做个网站wordpress 宽版
  • 做微新闻怎么发视频网站三台县城乡建设网网站
  • 长沙做php的网站建设泰安吧百度贴吧
  • 怎么做网站动图html手机版网站