一段markdown编辑器代码研究
说明
代码在 https://github.com/dukeofharen/markdown-editor
之所以选择这个来分析是一方面是因为它的代码结构比较简单,易于扩展,可塑性强;另一方面,自己可以跟着教程边做边学,掌握js的相关知识。
以下内容整理自作者写的tutorial
源码文件结构
源代码有4个目录
- css用于存放样式
- html目录下的文件是index.html,是app的主页
- img是一些图片
- js里面放的是js的脚本
此外,还有一个package.json
文件,这个文件里面放的是配置信息,在程序运行前nwjs会加载package.json
。
先来看js目录
jquery目录保存压缩过的jquery源码。
taboverride.min.js这个不知道是什么,先空着。
editor.js
,main.js
,menu.js
是我们要重点分析的文件。editor.js
是编辑器的脚本,main.js
是主程序,menu.js
是菜单的脚本。
先来看index.html,我截取了最重要的几行代码
...
<script src="../js/jquery/jquery.min.js" type="text/javascript"></script>
<script type="text/javascript" src="../js/main.js"></script>
<script type="text/javascript" src="../js/taboverride/taboverride.min.js"></script>
<script type="text/javascript">
global.window = window;
global.$ = $;
global.gui = require('nw.gui');
init();
</script>
...
前几行加载了需要的js文件,接着在页面里嵌入了几行js代码。
global.window = window;
global.$ = $;
global.gui = require('nw.gui');
第一行代码的意义是将DOM中的窗口对象window存放于global中。
第二行代码是把jquery对象存放在global变量中。
第三行是把nw.js中的gui对象存放在global中。
之所以这样做,是因为node.js的上下文中访问不到这三个对象,所以要把这三个对象存放在一个全局变量中。
最后,调用了main.js
中的init()
方法。
接下来让我们先来看main.js
中的init()
方法。
function init(){
var menu = require("./../js/menu.js");
menu.initMenu();
global.$(global.window.document).ready(function(){
var editor = require("./../js/editor.js");
if(global.gui.App.argv.length > 0){
editor.loadFile(global.gui.App.argv[0]);
}
var textEditor = global.$('#editor');
textEditor.bind('input propertychange', function() {
editor.reload();
});
tabOverride.set(global.window.document.getElementsByTagName('textarea'));
});
}
首先init函数加载了menu.js
模块。require相当于import,不同的是因为js是动态脚本语言,所以可以用到时再加载。获取menu对象后,调用了menu对象的initMenu
方法,初始化菜单栏。
接着,调用jquery选择了window.document用于获取页面,并给ready()
函数传入一个回调函数,意义是当页面加载完毕时,执行回调函数里面的内容。所谓回调函数,js的api文档是这么解释的:
A callback is a function that is passed as an argument to another function and is executed after its parent function has completed.
意思就是回调函数是 被作为参数传递给另一个父函数,且在父函数执行完毕后才开始执行的子函数。
那么回到正题来,回调函数里面干了什么事情呢?
1.
var editor = require("./../js/editor.js");
获取一个编辑器对象。
var textEditor = global.$('#editor');
用jquery的选择器选择index.html
中id=editor
的元素:
<textarea name="markdown" id="editor" class="md_editor"></textarea>
其实就是文本编辑区。
3.
textEditor.bind('input propertychange', function() {
editor.reload();
});
这段代码含义是给文本编辑区绑定一个事件,当检测到文本框内的内容改变时,就调用函数reload()
重新渲染右边的预览区。
让我们看看reload函数
exports.reload = function(){
var marked = require("marked");
marked.setOptions({
renderer: new marked.Renderer(),
gfm: true,
tables: true,
breaks: false,
pedantic: false,
sanitize: false,
smartLists: true,
smartypants: false
});
var resultDiv = global.$('.md_result');
var textEditor = global.$('#editor');
var text = textEditor.val();
resultDiv.html(marked(text));
};
1.首先加载marked
2.接着配置markdown解析器。
- gfm:应该是github flavored markdown的缩写
- tables:应该是允许使用表格
剩下的都不知道什么意思。。。
var resultDiv = global.$('.md_result');
从css中选择类.md_result
,这个类代表右边渲染出来的html网页。
4.获得文本编辑区的内容
.html
是jquery的函数,是对innerHTML
的封装,功能是设置或者获取DOM元素内的html内容。
这里我们用marked渲染文本,然后赋值给.md_result
类里面的内容。
exports相当于导出功能,使得其他文件可以用到这里面定义的方法。
exports.loadText = function(text){
var textEditor = global.$('#editor');
textEditor.val(text);
exports.reload();
};
loadText完成的功能是,用text替换掉文本编辑区的内容,目前还不支持多文件。