视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
编写一个非常简单的JavaScript编辑器
2020-11-27 20:24:30 责编:小采
文档

当然,我们已经有可以使用的很好的Web编辑器:你只需下载,并插入页面即可。我以前习惯于使用CodeMirror和ACE。例如,我为CodeMirror写了一个插件来支持PlantUML。然而,这些编辑器有一个问题:它们难以扩展和难以理解。

当我看到这些产品的代码时,有一些我不能轻易理解,有一些我没有自信可以在上面构建东西。

现在,我的哲学是构建简单的工具,可以工作,可以理解,可以组合和扩展。所以我想尝试另一种方法,从头开始构建一个简单的Web编辑器。

遵循用代码说明一切的原则,请看GitHub repo:https://github.com/ftomassetti/simple-web-editor

HTML

让我们从一些HTML代码开始:

<html> 
 <head>
 <link rel="stylesheet" type="text/css" href="css/style.css" media="screen" /> 
 <script src="js/jquery-3.1.1.min.js"></script>
 <script src="js/webeditor.js"></script>
 <link href="https://fonts.googleapis.com/css?family=Source+Code+Pro" rel="stylesheet"> 
 </head>
 <body>
 <h1>My Simple Web Editor</h1>
 <p id="editor">
 </p>
 <span class="blinking-cursor">|</span>
 <body>
</html>

我们需要做好哪些准备工作?

  • 当然首先是jquery

  • 一些CSS

  • Google提供的酷字体

  • 一个包含所有代码的JS文件(wededitor.js)

  • 一个p(编辑器)和一个用于编辑器的跨度(span)

  • TypeScript

    现在,我们要使用的是TypeScript,希望它可以减少使用JavaScript的痛苦。也因为我想尝试它。对于从未使用过TypeScript的人来说,从根本上说它就是JavaScript的超集,允许可选地指定类型。类型用于检查错误,然后被忘记,因为最终我们生成JavaScript。你可以在TypeScript中使用JavaScript库,并且当你想要使用JavaScript库的时候,你可能需要导入该库中所有类型的描述。这是我们在第一行代码中所导入的内容。

    /// <reference path="defs/jquery.d.ts" /> 
    class Editor { 
     private caretIndex: number; 
     private text: string; 
     constructor() {
     this.caretIndex = 0;
     this.text = "";
     }
     textBeforeCaret() {
     if (this.caretIndex == 0) {
     return "";
     } else {
     return this.text.substring(0, this.caretIndex);
     } 
     }
     textAfterCaret() {
     if (this.caretIndex == this.text.length) {
     return "";
     } else {
     return this.text.substring(this.caretIndex ); 
     }
     }
     generateHtml() {
     return this.textBeforeCaret()
     + "<span class='cursor-placeholder'>|</span>"
     + this.textAfterCaret();
     }
     type(c:string) {
     this.text = this.textBeforeCaret() + c + this.textAfterCaret();
     this.caretIndex = this.caretIndex + 1;
     }
     deleteChar() : boolean {
     if (this.textBeforeCaret().length > 0) {
     this.text = this.textBeforeCaret().substring(0, this.textBeforeCaret().length - 1) + this.textAfterCaret();
     this.caretIndex--;
     return true;
     } else {
     return false;
     }
     }
     moveLeft() : boolean {
     if (this.caretIndex == 0) {
     return false;
     } else {
     this.caretIndex--;
     return true;
     }
     }
     moveRight() : boolean {
     if (this.caretIndex == this.text.length) {
     return false;
     } else {
     this.caretIndex++;
     return true;
     }
     } 
    } 
    var updateHtml = function() {
     $("#editor")[0].innerHTML = (window as any).editor.generateHtml();
     var cursorPos = $(".cursor-placeholder").position();
     var delta = $(".cursor-placeholder").height() / 4.0;
     $(".blinking-cursor").css({top: cursorPos.top, left: cursorPos.left - delta});
    }; $( document ).ready(function() {
     (window as any).editor = new Editor();
     updateHtml();
     $(document).keypress(function(e){
     var c = String.fromCharCode(e.which);
     (window as any).editor.type(c);
     updateHtml();
     });
     $(document).keydown(function(e){
     if (e.which == 8 && (window as any).editor.deleteChar()) {
     updateHtml();
     };
     if (e.which == 37 && (window as any).editor.moveLeft()) {
     updateHtml();
     };
     if (e.which == 39 && (window as any).editor.moveRight()) {
     updateHtml();
     };
     });
    });

    好的,让我们来看看代码。我们有:

  • Editor类

  • 函数updateHTML

  • $(document).ready(…)格式的配线(wiring)

  • Editor类

    Editor类是我们要做文章下功夫的地方。这里我们存储两样东西:

  • 包含在编辑器中的文本

  • 文本中插入符的位置

  • TextBeforeCaret和TextAfterCaret显然允许我们得到所有文本之前或之后的插入符。

    那么,generateHTML做什么呢?它生成HTML代码,用于放置跨度以指示插入符位置的文本:此元素是插入符占位符。为什么我们不放置插入符本身呢?因为插入符有大小,所以如果我们在文本内部移动插入符,那么我们将导致所有的文本总是在移动。相反,我们移动大小为零的插入符占位符,然后我们使用插入符放置在插入符占位符上方,但在不同的z-index。通过这种方式,基本上我们就可以在我们想要看到的地方看到插入符,而不必左右移动文本就为了给插入符空出地方。

    其余的方法允许:

  • 插入字符

  • 删除字符

  • 向左移动插入符

  • 向右移动插入符

  • 函数updateHTML

    函数updateHTML实现了插入符的把戏:

    var updateHtml = function() {
     $("#editor")[0].innerHTML = (window as any).editor.generateHtml();
     var cursorPos = $(".cursor-placeholder").position();
     var delta = $(".cursor-placeholder").height() / 4.0;
     $(".blinking-cursor").css({top: cursorPos.top, left: cursorPos.left - delta});
    };

    首先我们更新编辑器的内容,然后我们找到插入符占位符的位置,然后我们移动位于占位符上方的闪烁光标(即占位符)。我们实际上会稍微向左移动一点占位符,因为这样看起来更好。

    配线(wiring)

    配线包括附加事件处理程序到:

  • 当我们键入字符的时候获取

  • 当我们删除字符的时候获取

  • 当我们使用左箭头和右箭头的时候获取

  • 然后我们从Editor类中调用方法。

    结论

    好的,让我们先简单的开始:一个非常小的编辑器,在这个编辑器中我们可以键入、删除和使用箭头移动。这不是最令人印象深刻的编辑器。但它简单,也可以工作。我们可以在此基础上建立一些机智的东西,去做我们要它做的事情,并且可理解和可扩展。

    下载本文
    显示全文
    专题