用 div 元素实现输入框的效果,并允许插入其他元素

有如下业务需求
在这里插入图片描述
在输入框中,允许插入其他标签元素。这在普通的 input 元素上是没法实现的,这里我们可以利用元素的 contenteditable 属性来实现此类效果。

基础样式代码:

<style>
  body {
    padding: 100px;
  }
  #input {
    border: 1px solid #ccc;
  }
  #input:focus {
    outline: 0;
    border-color: #409eff;
  }
  #input:empty:before {
    content: attr(placeholder);
    color: #cccccc;
  }
  #tip {
    color: #409eff;
    /* user-select: none; */
  }
  #btn {
    margin-top: 30px;
    user-select: none;
    display: inline-block;
  }
</style>

html 和 js:

  <body>
    <div id="input" contenteditable="true" placeholder="请输入">
      哇哈哈哈哈哈<span id="tip" contenteditable="false">#标签#</span>
    </div>
    <input id="btn" type="button" value="添加标签" contenteditable="false" />

    <script>
      let inputId = '';

      // 不使用click的原因是:点击按钮后会导致光标丢失。事件执行顺序: mousedown -> blur(输入框的事件) -> mouseup -> click
      document.getElementById('btn').addEventListener('mousedown', mousedown);
      document.getElementById('btn').addEventListener('mouseup', () => {
        console.log('mouseup');
      });
      document.getElementById('btn').addEventListener('click', () => {
        console.log('click');
      });
      function mousedown(e) {
        e.preventDefault(); // 防止光标消失, 阻止执行输入框的 blur 事件
        if (inputId !== 'input') {
          // 只有聚焦的元素是input时才执行
          return;
        }
        const selection = window.getSelection();
        console.log('mousedown', selection);
        // 没有光标或选区时 getRangeAt(0) 会报错
        let range;
        try {
          range = selection.getRangeAt(0);
        } catch (e) {}
        if (range) {
          const htmlStr = `<span id="tip" contenteditable="false">#标签#</span>`;
          const node = range.createContextualFragment(htmlStr); // 将 HTML 字符串转换为文档片段
          range.deleteContents(); // 删除选中的内容
          range.insertNode(node); // 插入文本
          range.collapse(false); // 焦点移动到插入文本后
          range.detach(); // 释放range
        }
      }

      document.getElementById('input').addEventListener('blur', blur);
      // 自动匹配文本中的内容并转换为span标签
      function blur(e) {
        inputId = '';
        const text = e.srcElement.innerText || '';
        console.log('blur', text);
        const ruleString = `(?<!<span id=\"tip\" contenteditable=\"false\">)(#标签#)(?!<\/span>)`;
        const pattern = new RegExp(ruleString, 'g');
        const newString = text.replace(pattern, '<span id="tip" contenteditable="false">$1</span>');
        e.srcElement.innerHTML = newString;
      }
      document.getElementById('input').addEventListener('click', (e) => {
        inputId = e.target.id;
      });
    </script>
  </body>

其中需要特别注意的几点:

  • inputId 记录当前聚焦的元素,判断是否是需要插入标签的元素
  • 允许用户手动输入 #标签# ,失去焦点后自动回显

涉及到的一些 api 可以去 MDN 上了解:

  • 17
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Vue,自定义指令是一种可重用的功能,允许你将DOM元素的行为抽象成指令,可以用于处理各种交互、视觉效果和数据操作等操作。 Vue自定义指令的实现方式如下: 1.使用`Vue.directive()`方法来创建一个全局指令。示例代码如下: ```javascript // 注册一个名为 v-focus 的全局指令 Vue.directive('focus', { // 当被绑定的元素插入到 DOM 时…… inserted: function (el) { // 聚焦元素 el.focus() } }) ``` 2.在组件使用自定义指令。示例代码如下: ```vue <template> <div> <input v-focus> </div> </template> ``` 上面的代码,`v-focus`是我们刚刚定义的自定义指令,它会在输入框插入到DOM时自动聚焦。 除了全局指令外,我们还可以在组件内部定义局部指令,示例代码如下: ```vue <template> <div v-my-directive></div> </template> <script> export default { directives: { 'my-directive': { bind (el, binding, vnode) { // 指令绑定时的处理逻辑 }, inserted (el, binding, vnode) { // 指令插入到DOM时的处理逻辑 }, update (el, binding, vnode, oldVnode) { // 指令更新时的处理逻辑 }, componentUpdated (el, binding, vnode, oldVnode) { // 指令所在组件更新完毕时的处理逻辑 }, unbind (el, binding, vnode) { // 指令与元素解绑时的处理逻辑 } } } } </script> ``` 在上面的代码,我们定义了一个名为`my-directive`的局部指令,并实现了`bind`、`inserted`、`update`、`componentUpdated`和`unbind`等生命周期函数,用于处理指令的不同阶段的逻辑。 总之,自定义指令是Vue非常强大的功能,可以帮助我们更好地抽象和复用DOM元素的行为。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值