惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Microsoft Security Blog
Microsoft Security Blog
J
Java Code Geeks
GbyAI
GbyAI
aimingoo的专栏
aimingoo的专栏
L
LangChain Blog
I
InfoQ
D
Docker
F
Fortinet All Blogs
Y
Y Combinator Blog
Martin Fowler
Martin Fowler
月光博客
月光博客
B
Blog
Engineering at Meta
Engineering at Meta
T
Tailwind CSS Blog
罗磊的独立博客
博客园_首页
G
Google Developers Blog
Stack Overflow Blog
Stack Overflow Blog
Recent Announcements
Recent Announcements
D
DataBreaches.Net
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
B
Blog RSS Feed
IT之家
IT之家
V
V2EX

zodream梦想开源/个人编程日记

文件解析笔记-zodream梦想开源/个人编程日记 密码本开发笔记之读写与保存-zodream梦想开源/个人编程日记 SkiaSharp 把 pixel byte[] 转成 SKBitmap-zodream梦想开源/个人编程日记 nas 使用 Docker 安装 gogs-zodream梦想开源/个人编程日记 复制 android 手机中的文件到电脑-zodream梦想开源/个人编程日记 周报:寻找优质的周刊-zodream梦想开源/个人编程日记 开发日志:对Markdown的代码块新增引用来源支持-zodream梦想开源/个人编程日记 周报:怎么写技术类的教程文章-zodream梦想开源/个人编程日记 css display:flex 布局尺寸超出问题-zodream梦想开源/个人编程日记 周报:SEO优化的思考-zodream梦想开源/个人编程日记 Edge 浏览器不适用 Edge Image Viewer 打开图片 -zodream梦想开源/个人编程日记 SEO 学习笔记(一) 内容来源-zodream梦想开源/个人编程日记 PHP 实现双因素身份认证(2FA)-zodream梦想开源/个人编程日记 WPF MVVM 获取List 多选数据-zodream梦想开源/个人编程日记 Burp Suite 抓包-zodream梦想开源/个人编程日记 使用 indexnow 注意事项-zodream梦想开源/个人编程日记 Godot 使用字体图标 例如: Iconfont、FontAwesome-zodream梦想开源/个人编程日记 angular 15 对指定页面进行访问限制-zodream梦想开源/个人编程日记 CSS 使用 column-count 实现瀑布流出现内容分割的解决办法-zodream梦想开源/个人编程日记 input 确认按键事件在手机端不生效-zodream梦想开源/个人编程日记 C# 使用socket 进行通讯-zodream梦想开源/个人编程日记 Maui开发中Windows应用开启管理员权限-zodream梦想开源/个人编程日记 Maui 中自定义控件-zodream梦想开源/个人编程日记 angular 14 使用 ng-template 实现tree 结构显示-zodream梦想开源/个人编程日记 c# 动态安装和卸载dll-zodream梦想开源/个人编程日记 慎用 CompositionTarget.Rendering-zodream梦想开源/个人编程日记 c# 重写 c++ 程序笔记:数据初始化-zodream梦想开源/个人编程日记 源码编译 aseprite-zodream梦想开源/个人编程日记 记录一下字符串分隔split各语言之间的不同-zodream梦想开源/个人编程日记 c# Gzip解码无头内容-zodream梦想开源/个人编程日记
js 进行在线编辑器开发-zodream梦想开源/个人编程日记
zodream · 2023-05-15 · via zodream梦想开源/个人编程日记

js 进行在线编辑器开发

基于 textarea 开发的 markdown 编辑器

textarea 最简单,但是可以编辑的内容也少。

需要的了解的知识

const element: HTMLTextAreaElement;

element.selectionStart // 获取或设置选中的起始位置
element.selectionEnd // 获取或设置选中的结束位置

// 获取选中的文字

const v = element.value;
const selectValue = v.substring(element.selectionStart, element.selectionEnd);

// 替换选中的内容

const replace = '';
element.value = v.substring(0, element.selectionStart) + replace + v.substring(element.selectionEnd);

// 移动光标到指定位置,移动光标到开始位置

element.selectionStart = 0 
element.selectionEnd = 0

// 移动光标到结尾

element.selectionStart = element.value.length
element.selectionEnd = element.value.length

// 需要把焦点设置到元素,才会显示光标
element.focus();

12345678910111213141516171819202122232425262728

textarea 只接受字符串,所以图片,超链接等需要使用 markdown 格式转成对应的字符串

基于 div 开发的 富文本编辑器

把div 设置为可编辑模式

<div contentEditable="true"></div>

1

需要了解的知识

  1. 默认换行是自动添加 '<div><br></div>'
  2. 获取选中
const element: HTMLDivElement;

const sel = window.getSelection();
const range = sel.getRangeAt(0); 
// range 就是当前选中的内容了
range.startContainer // 选中的起始节点
range.startOffset   // 在起始节点的具体位置
range.endContainer  // 选中的结束节点
range.startOffset   // 在结束节点的具体位置

// 当未选中任何内容时
range.startContainer === range.endContainer && range.startOffset === range.endOffset

range.startContainer // 节点的类型,有 Text 、HtmlElement
range.startOffset  // 当 节点类型为 Text 时,则为字符串的位置, 当为 HtmlElement 时,则为在节点中子元素的位置, 例如0 则是元素的最前面

// 选中 区域
const sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(selectRange);

// 选中指定元素

const selectRange = document.createRange();
// 选中整个元素
selectRange.selectNodeContents(element);
// 选中元素的一部分
selectRange.setStart(element, 0);
selectRange.setEnd(element, 0);

sel.removeAllRanges();
sel.addRange(selectRange);

// 截断字符串节点
const node: Text
node.splitText(offset) // 返回新创建的后一部分字符串的节点, 自动会添加到页面上的

12345678910111213141516171819202122232425262728293031323334353637

基于 div 开发的 代码编辑器

相对来说,代码编辑器相对简单,就只要 显示对应行号、对部分字符串加样式即可,当然更高级的需要加代码提示候选就更复杂点了。

const element: HtmlDivElement;

element.contentEditable = 'true';

123

基本逻辑

  1. 对回车进行换行处理,实际就是在选择的地方切断,把后面一部分放到另一行
  2. 新增行,需要同时增加行号,及在行号列表中增加新的一行,并同时个更新后面的行号
  3. 对行的高度要跟行号的高度进行同步
  4. 缩进或者文字输入需要提取整行内容进行处理
let isComposition = false; // 判断是否是输入法输入

element.addEventListener('keydown', e => {
});
element.addEventListener('keyup', () => {
    if (isComposition) {
        return;
    }

});
element.addEventListener('compositionstart', () => {
   isComposition = true;

});
element.addEventListener('compositionend', () => {
    isComposition = false;

});

123456789101112131415161718

  1. div 的尺寸变化要进行行号高度更新
let lastHeight = 0;
const resizeObserver = new ResizeObserver(entries => {
    for (const item of entries) {
        if (item.contentRect.height === lastHeight) {
            continue;
        }
        if (lastHeight === 0) {
            // 这里的意思是, 显示隐藏切换时进行高度更新
            this.updateLineNoStyle();
        }
        lastHeight = item.contentRect.height;
    }
});
resizeObserver.observe(element);

1234567891011121314

转载请保留原文链接: https://zodream.cn/blog/id/243.html