编程 如何利用JavaScript开发一个兼容PC和移动端的富文本编辑器

2024-11-19 04:21:14 +0800 CST views 542

利用 JavaScript 实现富文本编辑器

在近期的项目中,我需要开发一个兼容 PC 和移动端的富文本编辑器,并且包含一些特殊的定制功能。经过对现有 JavaScript 富文本编辑器的调研,发现大多数适用于桌面端的工具,如 UEditor,但很少有适用于移动端的。然而,由于我们不打算考虑过多的兼容性问题,所以决定自研一个轻量级的富文本编辑器。本文将介绍如何实现富文本编辑器,并分享在不同浏览器和设备之间遇到的一些问题及解决方案。

准备阶段

现代浏览器已经提供了许多 API 支持 HTML 的富文本编辑功能,因此我们不需要从零开始实现全部内容。

contenteditable="true"

首先,我们需要让一个 div 成为可编辑状态,只需添加 contenteditable="true" 属性即可。

<div contenteditable="true" id="rich-editor"></div>

在这样的 div 中插入的任何节点默认都是可编辑的。如果想插入不可编辑的节点,则需要指定插入节点的属性为 contenteditable="false"

光标操作

作为富文本编辑器,开发者需要能够控制光标的状态和位置信息。浏览器提供了 selection 对象和 range 对象来操作光标。

selection 对象

Selection 对象表示用户选择的文本范围或插入符号的当前位置。通常情况下,我们不会直接操作 selection 对象,而是通过操作与之对应的 range 对象来控制用户的选择区域。

获取 selection 对象的方式如下:

let selection = window.getSelection();

通过 selection 对象可以获得用户的 range 对象:

let range = selection.getRangeAt(0);

range 对象表示文档中的一段内容,包括起始节点和结束节点。操作 range 对象是控制光标的重点。

操作 range 对象

range 对象提供了一系列方法来操作光标位置,例如 setStart()setEnd()collapse() 等。通过这些方法,我们可以精确地控制光标的位置和选择范围。例如,将光标移动到指定位置:

let range = window.getSelection().getRangeAt(0);
let textEle = range.commonAncestorContainer;
range.setStart(range.startContainer, textEle.length);
range.setEnd(range.endContainer, textEle.length);

修改光标位置

有时需要强制修改光标位置,可以通过重新创建一个 range 对象,并删除所有现有的 ranges

function resetRange(startContainer, startOffset, endContainer, endOffset) {
    let selection = window.getSelection();
    selection.removeAllRanges();
    let range = document.createRange();
    range.setStart(startContainer, startOffset);
    range.setEnd(endContainer, endOffset);
    selection.addRange(range);
}

修改文本格式

实现富文本编辑器的关键在于能够修改文本格式。DOM 提供了 document.execCommand 方法,该方法允许运行命令来操作可编辑区域的内容。常见的命令包括加粗、斜体、文本颜色、列表等。

bool = document.execCommand(aCommandName, aShowDefaultUI, aValueArgument);

例如,加粗文本的命令如下:

document.execCommand('bold', false, null);

实战开始,填坑的旅途

在开发过程中,我们遇到了一些浏览器默认行为的问题,需要进行修正。

回车换行

在可编辑框中输入内容并回车换行后,浏览器生成的节点结构可能与预期不符。为了解决这个问题,可以在初始化时向 div 中插入一个 <p><br></p> 元素,并在内容清空后重新加入该结构。

插入 ulol 位置错误

调用 document.execCommand("insertUnorderedList", false, null) 来插入列表时,新的列表可能会被插入到 <p> 标签中。为此,我们需要在调用该命令前做一次修正:

function adjustList() {
    let lists = document.querySelectorAll("ol, ul");
    for (let i = 0; i < lists.length; i++) {
        let ele = lists[i];
        let parentNode = ele.parentNode;
        if (parentNode.tagName === 'P' && parentNode.lastChild === parentNode.firstChild) {
            parentNode.insertAdjacentElement('beforebegin', ele);
            parentNode.remove();
        }
    }
}

插入分割线

插入 <hr> 标签后,需要在其后追加一个空的文本节点或 <p><br></p>,并将光标定位在其中:

function insertHr() {
    let range = document.createRange();
    let hr = document.createElement('hr');
    range.insertNode(hr);
    range.setEndAfter(hr);
    range.setStartAfter(hr);
}

移动端优化

在移动端,富文本编辑器的问题主要集中在光标和键盘上。

自动获取焦点

在 iOS 中,为了安全考虑,代码无法自动获取焦点,需要用户点击。可以通过以下方法在 WebView 中移除这一限制:

[self.appWebView setKeyboardDisplayRequiresUserAction:NO];

处理 paste 粘贴

在富文本编辑器中,粘贴内容时默认会保留格式。为了更好地控制粘贴内容,可以监听 paste 事件,并使用 clipboardData 对象获取剪贴板中的文本或 HTML:

let plainText = event.clipboardData.getData('text/plain');
let plainHTML = event.clipboardData.getData('text/html');

其他功能

在实际项目中,还可能遇到如待办列表、附件卡片、Markdown 切换等需求。了解了 range 对象的操作后,这些问题都可以轻松解决。

复制全文 生成海报 前端开发 JavaScript Web技术

推荐文章

百度开源压测工具 dperf
2024-11-18 16:50:58 +0800 CST
jQuery `$.extend()` 用法总结
2024-11-19 02:12:45 +0800 CST
程序员出海搞钱工具库
2024-11-18 22:16:19 +0800 CST
PyMySQL - Python中非常有用的库
2024-11-18 14:43:28 +0800 CST
html一个包含iPhoneX和MacBook模拟器
2024-11-19 08:03:47 +0800 CST
html文本加载动画
2024-11-19 06:24:21 +0800 CST
Rust 并发执行异步操作
2024-11-18 13:32:18 +0800 CST
PHP 压缩包脚本功能说明
2024-11-19 03:35:29 +0800 CST
Nginx rewrite 的用法
2024-11-18 22:59:02 +0800 CST
Go 单元测试
2024-11-18 19:21:56 +0800 CST
thinkphp swoole websocket 结合的demo
2024-11-18 10:18:17 +0800 CST
Elasticsearch 聚合和分析
2024-11-19 06:44:08 +0800 CST
PHP 代码功能与使用说明
2024-11-18 23:08:44 +0800 CST
12个非常有用的JavaScript技巧
2024-11-19 05:36:14 +0800 CST
Go 中的单例模式
2024-11-17 21:23:29 +0800 CST
Plyr.js 播放器介绍
2024-11-18 12:39:35 +0800 CST
前端项目中图片的使用规范
2024-11-19 09:30:04 +0800 CST
为什么要放弃UUID作为MySQL主键?
2024-11-18 23:33:07 +0800 CST
Grid布局的简洁性和高效性
2024-11-18 03:48:02 +0800 CST
php微信文章推广管理系统
2024-11-19 00:50:36 +0800 CST
程序员茄子在线接单