首页 话题 小组 问答 好文 用户 我的社区 域名交易 唠叨

[分享]如何在 contenteditable 中实现输入控制?

发布于 2024-08-25 10:06:28
0
116

contenteditable 中 beforeinput 事件无法取消之谜

你是否也曾被 contenteditable 中的 beforeinput 事件搞得一头雾水?明明文档说可以取消,实际操作却毫无作用?最近在开发一个富文本编辑器时,我也遇到了这个棘手的问题。

事情是这样的,WebKit 官方博客信誓旦旦地表示,我们可以用 beforeinput 事件来控制 contenteditable 元素中的用户输入,甚至信誓旦旦地说可以取消。可是,当我满怀期待地在 Chrome 浏览器中测试他们的示例代码时,现实却给了我沉重的一击——压根不起作用!

追根溯源:Chrome 的“小秘密”

经过一番抽丝剥茧的调查,我终于找到了问题的根源:虽然 WebKit 博客言之凿凿,但 Chrome 浏览器压根还没完全实现这个功能!也就是说,在当前版本的 Chrome 中,beforeinput 事件的 cancelable 属性就是个摆设,无法阻止用户的输入。

柳暗花明:替代方案

既然 beforeinput 事件这条路走不通,我们就得另辟蹊径了。好在天无绝人之路,我们可以用其他方法来实现对 contenteditable 元素的输入控制。

方案一:亡羊补牢,为时未晚——input 事件监听与回滚

既然无法阻止输入,那我们就等输入完成后再进行判断,如果发现不符合预期,直接回滚到之前的状态即可。

具体做法是监听 input 事件,在事件触发后检查输入内容是否符合要求。如果符合,那就万事大吉;如果不符合,就通过操作 DOM 将内容还原到之前的状态。

这种方法简单直接,但需要手动处理 DOM 更新,可能会影响性能,特别是需要频繁进行输入校验时。

const editor = document.getElementById('editor');

editor.addEventListener('input', (event) => {
  // 检查输入内容是否符合预期
  if (isInputValid(event.target.innerHTML)) {
    // 输入有效,不做任何处理
    return;
  }

  // 输入无效,回滚到之前的状态
  event.target.innerHTML = getPreviousState();
});

// 模拟存储上一次有效状态
function getPreviousState() {
  // 此处需要根据实际情况实现,比如可以使用 history.state 或者自定义变量来存储上一次的有效状态
  return '上一次有效状态';
}

// 检查输入内容是否符合预期
function isInputValid(input) {
  // 此处需要根据实际情况实现,比如可以使用正则表达式或者其他校验规则来判断输入是否有效
  return input === '有效输入';
}

方案二:防患于未然——键盘事件监听与阻止默认行为

俗话说,最好的防守就是进攻。与其事后补救,不如从源头上掐断“祸根”。

对于某些特定按键触发的输入,我们可以监听键盘事件(比如 keydown),然后使用 event.preventDefault() 阻止默认行为。

这种方法的优点是精细度高,可以针对特定按键进行控制,缺点是需要处理各种按键组合,逻辑可能较为复杂,尤其是需要兼容各种浏览器和输入法时。

const editor = document.getElementById('editor');

editor.addEventListener('keydown', (event) => {
  // 检查是否为需要阻止的按键
  if (shouldPreventKey(event.key)) {
    event.preventDefault();
  }
});

// 检查是否为需要阻止的按键
function shouldPreventKey(key) {
  // 此处需要根据实际情况实现,比如可以根据按键的 keyCode 或者 key 值来判断
  return key === 'Enter'; // 阻止用户输入回车
}

方案三:借力打力——使用第三方库

正所谓“术业有专攻”,有些事情自己做比较麻烦,但借助专业的工具却能事半功倍。

市面上有很多专门用于处理 contenteditable 元素的第三方库,比如 ProseMirror、Slate 等,它们提供了更完善的输入控制机制,可以让我们更方便地实现所需功能。

这些库通常封装了底层的 DOM 操作和事件处理逻辑,并提供了更高级的 API,可以大大简化开发过程。

以下是一个使用 ProseMirror 库实现输入控制的示例:

import { EditorState } from 'prosemirror-state';
import { EditorView } from 'prosemirror-view';
import { Schema, DOMParser } from 'prosemirror-model';

// 定义文档结构
const schema = new Schema({
  nodes: {
    doc: {
      content: 'paragraph+'
    },
    paragraph: {
      content: 'text*',
      toDOM() { return ['p', 0] }
    },
    text: {
      group: 'inline'
    }
  }
});

// 创建编辑器状态
const state = EditorState.create({
  schema: schema,
  doc: DOMParser.fromSchema(schema).parse(document.getElementById('editor'))
});

// 创建编辑器视图
const view = new EditorView(document.getElementById('editor'), {
  state: state,
  // 定义输入规则
  dispatchTransaction(tr) {
    // 在这里可以对输入进行校验和过滤
    this.updateState(this.state.apply(tr));
  }
});

条条大路通罗马:选择适合你的方案

虽然 beforeinput 事件在 contenteditable 元素中的取消功能暂时还没有完全实现,但我们仍然可以通过其他方法来实现对用户输入的控制。选择哪种方案取决于具体的业务需求和项目复杂度。

如果只是简单的输入校验,可以使用 input 事件监听和回滚的方式;如果需要更精细的控制,可以使用键盘事件监听和阻止默认行为;如果需要更强大的功能和更方便的 API,建议使用第三方库。

评论
一个月内的热帖推荐
啊龙
Lv.1普通用户

9545

帖子

31

小组

3242

积分

赞助商广告
站长交流