• 全部
  • 问答

可编辑DIV插入标签的方法。

Libaiquzb 2021-09-08 11:25:34

React.js

萌新刚入职,碰到一个抽象的需求,要在内容输入框中写文字的时候点击边上的其他标签可以插入到内容中的光标位置,翻看了许多网页都没有想要的解决方法,综合了一下,结合Range和 window.getSelection();整理了如下解决方法。

1、render中写组件

 <div

                    id="divtextarea"

                    placeholder="请输入您的内容"

                    contentEditable

                    style={{ height: '150px', border: '1px solid #d9d9d9', padding: ' 0 2px', outline: 'none', overflowY: 'auto' }}

                    tabIndex="0"

                    onFocus={this.handleChange}

                  />

2、onFocus方法代替textarea的onchange方法

 // 监听光标变化代替onChange  

handleChange = () => {
    document.onselectionchange = () => {
      let sel = null;

      let value = document.getElementById('divtextarea').innerHTML;

      value = value.replace(/<span.*?>/g, '${');

      value = value.replace(/<\/span.*?>/g, '}');

      if (document.selection) {
        sel = document.selection;

      } else {
        sel = window.getSelection();

// 记录光标位置判断是否在div上

        if (sel.rangeCount > 0 && (sel.anchorNode.id === 'divtextarea' || sel.anchorNode.parentNode.id === 'divtextarea')) {
          this.setState({
            nrValue: value,

          });

          const { changezt } = this.props;

          if (changezt) {
            changezt(value);

          }

          console.log(value);

        }

      }

    };

  }

3、写标签插入的方法

 // 往内容中插入span标签

  insertContent = (content) => {
    if (!content) {
      return;

    }

    let sel = null;

    if (document.selection) {
      sel = document.selection;

      sel.createRange().pasteHTML(content);

    } else {
      sel = window.getSelection();

// 同上考虑到标签插入时光标会在之前插入过的标签内或者不在DIV上,这里作一下判断

// 光标在div的text上时后层组件是div,在插入标签时后两层是div,这样就不会插入到div外面或者出现标签内插入标签。

      if (sel.rangeCount > 0 && (sel.anchorNode.id === 'divtextarea' || sel.anchorNode.parentNode.id === 'divtextarea')) {
        const range = sel.getRangeAt(0);

        range.deleteContents();

        const el = document.createElement('span');

        el.innerHTML = content;

        const frag = document.createDocumentFragment();

        const node = el.firstChild;

        const lastNode = frag.appendChild(node);

        range.insertNode(frag);

        const contentRange = range.cloneRange();

        contentRange.setStartAfter(lastNode);

        contentRange.collapse(true);

        sel.removeAllRanges();

        sel.addRange(contentRange);

      }

    }

  }

 

4、调用(在需要调用的方法里面加入下面两行代码就行了)

 const imgTag = `<span contenteditable="false" style="background-color: #e8e8e8; border-radius: 4px; margin : 0 3px">${需要插入的内容}</span>`;

    this.insertContent(imgTag);

// 考虑到后台保存数据时不能出现标签,做一个正则替换

    let value = document.getElementById('divtextarea').innerHTML;

    value = value.replace(/<span.*?>/g, '${');

    value = value.replace(/<\/span.*?>/g, '}');

    value = value.replace(/<br>/g, '');

 

...全文
99 4 收藏 1
写回复
1 条回复
切换为时间正序
请发表友善的回复…
发表回复
Libaiquzb 09-08

因为使用的是window.getSelectionfang方法获取光标,所以避免标签乱插入,考虑过给插入的span标签加入user-select: none;属性来防止光标选中,但是contenteditable="true" 属性的标签中加这个属性会出现bug,当最后连续插入标签的时候删除会直接清空div所有内容,所以这里用光标后层标签是否是divtextarea来代替。

回复 2
相关推荐
发帖
JavaScript
创建于2007-09-28

8.5w+

社区成员

Web 开发 JavaScript
申请成为版主
帖子事件
编辑了帖子
2021-09-10 10:56
创建了帖子
2021-09-08 11:25
社区公告
暂无公告