js 进行在线编辑器开发

Fork Me On Github

js 进行在线编辑器开发

基于 textarea 开发的 markdown 编辑器

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

需要的了解的知识

ts
                            
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 设置为可编辑模式

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

需要了解的知识

  1. 默认换行是自动添加 '<div><br></div>'
  2. 获取选中
ts
                                     
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 开发的 代码编辑器

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

ts
   
const element: HtmlDivElement;

element.contentEditable = 'true';
123

基本逻辑

  1. 对回车进行换行处理,实际就是在选择的地方切断,把后面一部分放到另一行
  2. 新增行,需要同时增加行号,及在行号列表中增加新的一行,并同时个更新后面的行号
  3. 对行的高度要跟行号的高度进行同步
  4. 缩进或者文字输入需要提取整行内容进行处理
ts
                  
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 的尺寸变化要进行行号高度更新
ts
              
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
点击查看全文
0 135 0