这是js写的富文本编辑器,还存在一些bug,但基本功能已经实现,通过这个练习,巩固了js富文本编辑方面的知识,里面包含颜色选择器、全屏、表情、上传图片等功能,每个功能实际对应的就是一个小插件啦

部分程序:

    var RichEditor = function(container, params) {
params = params || {};
var options = {
width: 900,
height: 500,
borderColor: "#ddd",
buttons: {
heading: {
title: "标题",
icon: "\uf1dc",
click: function() {
var h = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
r.closeModal();
var html = '<div class="editor-heading">';
h.forEach(function(h) {
html += '<' + h + ' data-h="' + h + '">' + h + '</' + h + '>';
});
html += '</div>'; function HClick() {
var h = document.querySelector('.editor-heading');
h = h.childNodes;
/*console.log('h',h);*/
/*修改,迭代Nodelist最好使用length属性初始化第二个变量,避免无限循环*/
for(var i=0, len=h.length; i < len; i++){
addEvent(h[i], 'click', function() {
var h = this.getAttribute('data-h');
r.execCommand('formatBlock', '<' + h + '>'); /*formatBlock使用指定的HTML标签来格式化选择的文本块*/
r.closeModal();
}, false);
}
/*h.forEach(function(v) {
addEvent(v, 'click', function() {
var h = this.getAttribute('data-h');
r.execCommand('formatBlock', '<' + h + '>');
r.closeModal();
}, false);
});*/
};
r.openModal.call(this, html, HClick);
}
},
code: {
title: "引用",
icon: "\uf10d",
click: function() {
var html='<blockquote class="editor-block"><p><br></p></blockquote>';
r.execCommand('insertHTML',html);
var p=document.createElement('p');
p.innerHTML='<br>';
et.appendChild(p);
}
},
bold: {
title: "加粗",
icon: "\uf032",
click: function() {
r.execCommand('bold');
}
},
italic: {
title: "斜体",
icon: "\uf033",
click: function() {
r.execCommand('italic');
}
},
underline: {
title: "下划线",
icon: "\uf0cd",
click: function() {
r.execCommand('underline');
}
},
strikethrough: {
title: "删除线",
icon: "\uf0cc",
click: function() {
r.execCommand('strikethrough');
}
},
foreColor: {
title: "字体颜色",
icon: "\uf1fc",
click: function() {
var color = new r.colorPicker('foreColor');
r.openModal.call(this, color.addColorBoard(), color.clickEvent);
}
},
backColor: {
title: "背景色",
icon: "\uf043",
click: function() {
var color = new r.colorPicker('hiliteColor');
r.openModal.call(this, color.addColorBoard(), color.clickEvent);
}
},
justifyLeft: {
title: "居左",
icon: "\uf036",
click: function() {
r.execCommand('justifyLeft');
}
},
justifyCenter: {
title: "居中",
icon: "\uf037",
click: function() {
r.execCommand('justifyCenter');
}
},
justifyRight: {
title: "居右",
icon: "\uf038",
click: function() {
r.execCommand('justifyRight');
}
},
justifyFull: {
title: "两端对齐",
icon: "\uf039",
click: function() {
r.execCommand('justifyFull');
}
},
insertOrderedList: {
title: "有序列表",
icon: "\uf0cb",
click: function() {
r.execCommand('insertOrderedList');
}
},
insertUnorderedList: {
title: "无序列表",
icon: "\uf0ca",
click: function() {
r.execCommand('insertUnorderedList');
}
},
indent:{
title:"indent",
icon:"\uf03c",
click:function(){
r.execCommand('indent');
}
},
outdent:{
title:"outdent",
icon:"\uf03b",
click:function(){
r.execCommand('outdent');
}
},
createLink: {
title: "链接",
icon: "\uf0c1",
click: function() {
r.closeModal();
var html = '<input type="text" placeholder="www.example.com" class="editor-link-input"/> <button type="button" class="editor-confirm">确认</button>'; function btnClick() {
var confirm = document.querySelector('.editor-confirm');
addEvent(confirm, 'click', function() {
var link = document.querySelector('.editor-link-input');
if(link.value.trim() != '') { /*获取字符串副本*/
var a = '<a href="' + link.value + '" target="_blank">' + link.value + '</a>';
r.execCommand('insertHTML', a);
r.closeModal();
};
}, false);
};
r.openModal.call(this, html, btnClick);
}
},
insertImage: {
title: "插入图片",
icon: "\uf03e",
click: function() {
r.closeModal();
var html = '<div class="editor-file">图片上传<input type="file" name="photo" accept="image/*" class="editor-file-input"/></div>'; /*指定MIME类型为图像,以便在load事件中把它保存为数据URL*/
r.openModal.call(this, html, r.fileInput);
}
},
emotion: {
title: "表情",
icon: "\uf118",
click: function() {
r.closeModal();
r.drawEmotion.call(this);
}
},
fullscreen: {
title: "全屏",
icon: "\uf066",
click: function() {
r.toggleFullScreen();
}
},
save: {
title: "保存",
icon: "\uf0c7"
}
}
};
var selectedRange = null;
var originParams = {};
var et = null;
var toolbarTop = null;
for(var param in params) {
if(typeof params[param] === 'object' && params[param] != null) {
originParams[param] = {};
for(var deepParam in params[param]) {
originParams[param][deepParam] = params[param][deepParam];
};
} else {
originParams[param] = params[param];
}
};
for(var def in options) { /*遍历options,看传进来的params有没有options中的属性,有就覆盖*/
if(typeof params[def] === 'object') {
for(var deepDef in options[def]) {
if(typeof params[def][deepDef] === "object") {
for(var ddDef in options[def][deepDef]) {
if(typeof params[def][deepDef][ddDef] === 'undefined') {
params[def][deepDef][ddDef] = options[def][deepDef][ddDef];
}
};
} else if(def !== "buttons") {
params[def][deepDef] = options[def][deepDef];
}
};
} else if(typeof params[def] === 'undefined') {
params[def] = options[def];
}
};
//添加addEventlistener事件
var addEvent = function(element, type, handler, useCapture) {/*判断使用1级DOM还是2级DOM并兼容IE*/
if(element.addEventListener) {
element.addEventListener(type, handler, useCapture ? true : false);
} else if(element.attachEvent) {
element.attachEvent('on' + type, handler);
} else if(element != window){
element['on' + type] = handler;
}
};
var removeEvent = function(element, type, handler, useCapture) { /*移除事件监听*/
if(element.removeEventListener) {
element.removeEventListener(type, handler, useCapture ? true : false);
} else if(element.detachEvent) {
element.detachEvent('on' + type, handler);
} else if(element != window){
element['on' + type] = null;
}
};
// http://www.cristinawithout.com/content/function-trigger-events-javascript
/*没用*/
var fireEvent = function(element, type, bubbles, cancelable) {
if(document.createEvent) {
var event = document.createEvent('Event');
event.initEvent(type, bubbles !== undefined ? bubbles : true, cancelable !== undefined ? cancelable : false);
element.dispatchEvent(event);
} else if(document.createEventObject) { //IE
var event = document.createEventObject();
element.fireEvent('on' + type, event);
} else if(typeof(element['on' + type]) == 'function'){
element['on' + type]();
}
};
// prevent default
var cancelEvent = function(e) {
if(e.preventDefault){
e.preventDefault();
}
else{
e.returnValue = false;
}
if(e.stopPropagation){
e.stopPropagation();
}
else{
e.cancelBubble = true;
}
return false;
};
var r = this;
r.params = params;
r.originalParams = originParams;
r.drawTool = function(toolbarTop) {/*添加工具栏中的选项*/
var buttons = r.params.buttons;
for(var btn in buttons) {
var btnA = document.createElement("a");
btnA.className = "re-toolbar-icon";
btnA.setAttribute("title", buttons[btn]["title"]);
btnA.setAttribute("data-edit", btn);
btnA.innerHTML = buttons[btn]["icon"];
toolbarTop.appendChild(btnA);
};
};
/*表情*/
r.drawEmotion = function() {
var list_smilies = ['smile', 'smiley', 'yum', 'relieved', 'blush', 'anguished', 'worried', 'sweat',
'unamused', 'sweat_smile', 'sunglasses', 'wink', 'relaxed', 'scream', 'pensive',
'persevere', 'mask', 'no_mouth', 'kissing_closed_eyes', 'kissing_heart', 'hushed',
'heart_eyes', 'grin', 'frowning', 'flushed', 'fearful', 'dizzy_face', 'disappointed_relieved',
'cry', 'confounded', 'cold_sweat', 'angry', 'anguished', 'broken_heart', 'beetle', 'good', 'no', 'beer',
'beers', 'birthday', 'bow', 'bomb', 'coffee', 'cocktail', 'gun', 'metal', 'moon'
];
var html = ''; for(var i=0,len=list_smilies.length;i<len;i++){
html += '<img src="data:images/emotion/' + list_smilies[i] + '.png" class="emotion" width="20" height="20" alt="" />';
}
/*list_smilies.forEach(function(v) {
html += '<img src="data:images/emotion/' + v + '.png" class="emotion" width="20" height="20" alt="" />';
});*/
r.openModal.call(this, html); function add() { /*必须有服务器才能显示*/
console.log('this.src',this.src);
var img = '<img src="' + this.src + '" class="emotion" width="20" height="20" alt="" />';
document.execCommand('insertHTML', true, img);
r.closeModal();
};
var emotion = document.querySelectorAll('.emotion');
for(var i=0,len=emotion.length;i<len;i++){
addEvent(emotion[i], 'click', add, false);
}
/*emotion.forEach(function(e) {
addEvent(e, 'click', add, false);
});*/
};
/*全屏*/
r.toggleFullScreen = function() {
if(!document.fullscreenElement && !document.mozFullScreenElement && !document.webkitFullscreenElement) {
var docElm = document.documentElement;
if(docElm.requestFullscreen) {
docElm.requestFullscreen();
} else if(docElm.mozRequestFullScreen) {
docElm.mozRequestFullScreen();
} else if(docElm.webkitRequestFullScreen) {
docElm.webkitRequestFullScreen();
} else if(elem.msRequestFullscreen) {
elem.msRequestFullscreen();
};
} else {/*已经开启全屏*/
if(document.exitFullscreen) {
document.exitFullscreen();
} else if(document.mozCancelFullScreen) {
document.mozCancelFullScreen();
} else if(document.webkitCancelFullScreen) {
document.webkitCancelFullScreen();
} else if(document.msExitFullscreen) {
document.msExitFullscreen();
}
};
};
r.execCommand = function(command, param) {
r.selections.restoreSelection();
et.focus();
if(!arguments[1]) {
param = null;
};
document.execCommand(command, false, param);
};
r.selections = {
getCurrentRange: function() {/*获取选中的范围*/
//获取当前range
if(window.getSelection) {
//使用 window.getSelection() 方法获取鼠标划取部分的起始位置和结束位置
var sel = window.getSelection();
if(sel.rangeCount > 0){
//通过selection对象的getRangeAt方法来获取selection对象的某个Range对象
return sel.getRangeAt(0);
} } else if(document.selection) {/*如果没有window.getSelection*/
var sel = document.selection;
return sel.createRange();
}
return null;
},
saveSelection: function() {
selectedRange = r.selections.getCurrentRange();
},
restoreSelection: function() { //当你点击了工具条时,当前焦点也就改变了,所以我们需要恢复前一个焦点位置
var selection = window.getSelection(); /*获得selection对象*/
if(selectedRange) {
try {
selection.removeAllRanges(); /*移除选区*/
} catch(ex) {
document.body.createTextRange().select();
document.selection.empty();
};
selection.addRange(selectedRange); /*将指定的DOM范围添加到选区*/
}
},
getSelectionHTML: function() {
if(window.getSelection) {
var sel = window.getSelection();
if(sel.rangeCount > 0) {
return sel;
}
}
}
};
/*没用*/
var getSelectionRect = function() {
if(window.getSelection) {
var sel = window.getSelection();
if(!sel.rangeCount) {
return false;
}
var range = sel.getRangeAt(0).cloneRange();
}
};
/*上传文件*/
r.fileInput = function() {
var fi = document.querySelector('.editor-file-input'); function change(e) {
var files = e.target.files;
var file = null;
var url = null; /*var reader=new FileReader();
if(files && files.length > 0){
reader.readAsDataURL(files[0]);
console.log('reader.result',reader.result);
var img = '<img src="' + reader.result + '"/>';
document.execCommand('insertHTML', false, img);
}*/
if(files && files.length > 0) {
file = files[0];
try {
var fileReader = new FileReader();
fileReader.onload = function(e) {
url = e.target.result;
console.log('url',url);
var img = '<img src="' + url + '"/>';
document.execCommand('insertHTML', false, img);
/*document.execCommand('insertimage', false, url);*/
}
fileReader.readAsDataURL(file);
} catch(e) { }
}
r.closeModal();
};
fi.onchange = change;
};
r.toolClick = function() {
var toolbtn = document.querySelectorAll('a[data-edit]'); /*匹配CSS选择符,含All找所有*/
for(var i = 0; i < toolbtn.length; i++) {
addEvent(toolbtn[i], "click", function(e) {
var btn = r.params.buttons;
var name = this.getAttribute("data-edit");
if(typeof btn[name]["click"] !== 'undefined') { /*每个工具栏选项都含有一个click属性*/
r.selections.restoreSelection(); /*重置为上个range*/
btn[name].click.call(this); /*使用call方法扩充函数,调用btn[name].click函数*/
r.selections.saveSelection();
} else { }
e.stopPropagation();
}, false); /*冒泡阶段被调用*/
} };
r.getStyle = function(dom, attr) {/*getComputedStyle()方法,返回一个对象,其中包含当前元素的所有计算的样式;
IE不支持getComputedStyle()方法,在IE中每个具有style属性的元素还有一个currentStyle属性,
它包含当前元素全部计算后的样式*/
var value = dom.currentStyle ? dom.currentStyle[attr] : getComputedStyle(dom, false)[attr];
return parseFloat(value);
};
r.openModal = function(html, fn) { /*打开模态框*/
r.modal = document.createElement('div');
r.modal.className = 'editor-modal';
r.modal.innerHTML = html; /*每个模态框内容不同*/
r.parent.appendChild(r.modal);
var left = this.offsetLeft + (r.getStyle(this, 'width') - r.getStyle(r.modal, 'width')) / 2; /*按钮的宽度,模态框的宽度*/
left < 0 ? left = 3 : '';
r.modal.style.left = left + 'px';
if(fn) {
fn();
}
};
r.closeModal = function() { /*关闭模态框*/
if(r.modal != null) {
r.parent.removeChild(r.modal);
r.modal = null;
}
};
r.isInModal = function(e) {
if(r.modal != null) {
var node = e.target; /*点的谁就是谁*/
var isIn = false;
var modal = document.querySelector('.editor-modal');
while(typeof node !== 'undefined' && node.nodeName != '#document') {
if(node === modal) { /*判断点击的是不是模态框*/
isIn = true;
break;
}
node = node.parentNode;
};
if(!isIn) { /*点的模态框之外的范围,则关闭*/
r.closeModal();
}
}
};
r.init = function() {
r.parent = document.getElementById(container.replace("#", "")); /*替换掉#号*/
var defaultValue = r.parent.innerHTML;
r.parent.innerHTML = '';
r.parent.className += " re-container"; /*前面有空格*/
r.parent.style.boxSizing = "border-box";
r.parent.style.border = "1px solid " + r.params.borderColor;
r.parent.style.width = r.params.width + "px";
r.parent.style.height = r.params.height + "px";
et = document.createElement("div");
et.className = "re-editor"; /*位于工具栏下的文本编辑框*/
et.setAttribute("tabindex", 1);
et.setAttribute("contenteditable", true);/*contenteditable 属性的出现,让我们可以将任何元素设置成可编辑状态。*/
et.setAttribute('spellcheck', false);
et.innerHTML = defaultValue; /*将默认HTML写进该编辑框*/
toolbarTop = document.createElement("div");
toolbarTop.className = "re-toolbar re-toolbar-top"; /*工具栏*/
toolbarTop.style.backgroundColor = r.params.toolBg;
r.parent.appendChild(toolbarTop);
r.parent.appendChild(et);
r.drawTool(toolbarTop);
r.toolClick();
addEvent(window, 'click', r.isInModal, false);
addEvent(et, "keyup", function(e) {/*键盘鼠标up获取选区*/
r.selections.saveSelection();
}, false);
addEvent(et, "mouseup", function(e) {
r.selections.saveSelection();
}, false);
var addActiveClass = function() {
this.parentNode.classList.add('active');
};
var removeActiveClass = function() {
this.parentNode.classList.remove('active');
};
addEvent(et, "focus", addActiveClass);
addEvent(et, "blur", removeActiveClass); var topHeight = document.querySelector(".re-toolbar-top").offsetHeight; /*offsetHeight元素的高度*/
et.style.height = (r.params.height - topHeight) + "px";
};
/*颜色选择*/
r.colorPicker = function(command) {
var HSVtoRGB = function(h, s, v) {
var r, g, b, i, f, p, q, t;
i = Math.floor(h * 6);
f = h * 6 - i;
p = v * (1 - s);
q = v * (1 - f * s);
t = v * (1 - (1 - f) * s);
switch(i % 6) {
case 0:
r = v, g = t, b = p;
break;
case 1:
r = q, g = v, b = p;
break;
case 2:
r = p, g = v, b = t;
break;
case 3:
r = p, g = q, b = v;
break;
case 4:
r = t, g = p, b = v;
break;
case 5:
r = v, g = p, b = q;
break;
}
var hr = Math.floor(r * 255).toString(16); /*转换为16进制*/
var hg = Math.floor(g * 255).toString(16);
var hb = Math.floor(b * 255).toString(16);
return '#' + (hr.length < 2 ? '0' : '') + hr + /*转换为字符串*/
(hg.length < 2 ? '0' : '') + hg +
(hb.length < 2 ? '0' : '') + hb;
}; this.addColorBoard = function() {
var table = document.createElement('table');
table.setAttribute('cellpadding', 0);
table.setAttribute('cellspacing', 0);
table.setAttribute('unselectable', 'on');
table.style.border = '1px solid #d9d9d9';
table.setAttribute('id', 'color-board');
for(var row = 1; row < 15; ++row) // should be '16' - but last line looks so dark
{
var rows = document.createElement('tr'); /*行*/
for(var col = 0; col < 25; ++col) // last column is grayscale
{
var color;
if(col == 24) {/*使最后一列变灰,且随着行数的增加颜色越来越深*/
var gray = Math.floor(255 / 13 * (14 - row)).toString(16); /*(255/13)*13从255变到0*/
var hexg = (gray.length < 2 ? '0' : '') + gray; /*gray的值在不断往上叠加*/
color = '#' + hexg + hexg + hexg;
} else {
var hue = col / 24;
var saturation = row <= 8 ? row / 8 : 1;
var value = row > 8 ? (16 - row) / 8 : 1;
color = HSVtoRGB(hue, saturation, value);
}
var td = document.createElement('td'); /*列*/
td.setAttribute('title', color);
td.style.cursor = 'url(di.ico),crosshair';
td.setAttribute('unselectable', 'on');
td.style.backgroundColor = color;
td.width = 12;
td.height = 12;
rows.appendChild(td);
}
table.appendChild(rows);
};
var box = document.createElement('div');
box.appendChild(table);
return box.innerHTML;
};
this.clickEvent = function() {
var tds = document.getElementById('color-board');
tds = tds.childNodes[0].getElementsByTagName('td'); /*每个节点都有一个childNodes属性,其中保存着NodeList对象*/
for(var i = 0; i < tds.length; i++) {/*childNodes[0]是tbody,找到里面所有的td,且tbody是创建table后自动添加的*/
addEvent(tds[i], 'click', function() {
var color = this.getAttribute('title');
r.execCommand(command, color);
r.closeModal();
}, false);
}
}
}; r.init();
return r;
};

【JavaScript】富文本编辑器的更多相关文章

  1. 10个免费的javascript富文本编辑器(jQuery and non-jQuery)

    祝愿园子里的朋友圣诞节快乐. 本文介绍了10个免费易用富文本编辑器(rich text editors,RTE),其中5个是Jquery插件,另外5个是非Jquery富文本编辑器 简介 Javascr ...

  2. Javascript富文本编辑器

    分享几款Javascript富文本编辑器 ueditor jqframework xheditor htmlbox kindeditor wymeditor jhtmlarea markitup ck ...

  3. JavaScript 富文本编辑器

    WEB项目中使用UEditor(富文本编辑器) UEditor - 完整示例 http://ueditor.baidu.com/website/onlinedemo.html UEditor注意事项: ...

  4. 富文本编辑器TinyMCE

    最近项目中用到了javascript富文本编辑器,从网上找开源控件,发现很多可选,参考下面文章,列出了很多可用的插件http://www.cnblogs.com/ywqu/archive/2009/1 ...

  5. 富文本编辑器防止xss注入javascript版

    富文本编辑器:ueditor 其实富文本编辑器已经有防止xss注入功能,但是你服务端程序在接收的时候在做一次转义,否则有可能然后前端验证直接提交数据导致被xss攻击. 为了节省后端程序开销则在前端 显 ...

  6. Javascript实现简单的富文本编辑器

    <span style="font-size:14px;"><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 T ...

  7. wangEditor-基于javascript和css开发的 Web富文本编辑器, 轻量、简洁、易用、开源免费(2)

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  8. JavaScript Iframe富文本编辑器中的光标定位

    最近在项目中碰到一个比较棘手的问题: 在iframe富文本编辑器中,有个工具栏,这个工具栏在iframe标签之外,工具栏上有一个按钮,点击该按钮向iframe正在编辑中的光标处插入一个图片,图片会插入 ...

  9. [前端随笔][JavaScript] 制作一个富文本编辑器

    写在前面 现在网上有很多现成的富文本编辑器,比如百度家的UEditor,kindeditor,niceditor等等,功能特别的多,API也很多,要去熟悉他的规则也很麻烦,所以想自己了解一下原理,做一 ...

随机推荐

  1. nvm安装最新稳定版node

    安装当前最新的稳定版. nvm install stable

  2. [C#]LockBits使用笔记

    昨天想基于一张图片做个手机锁屏来着,原图如下:主要是嫌白底太丑了,一开始是想画图工具直接油漆桶伺候,然而一浇上去就发现问题了,变成了这样:看来得手工处理一下把底色统一了,原图分辨率挺高的,SetPix ...

  3. 前端(十一):props、state及redux关系梳理

    所谓状态机,是一种抽象的数据模型,是“事物发展的趋势”,其原理是事件驱动.广泛地讲,世界万物都是状态机. 一.状态机是一种抽象的数据模型 在react中,props和state都可以用来传递数据.这里 ...

  4. linux下配置环境变量方式

    linux下配置环境变量有多种方式,下面简述之 方式1.编辑 /etc/profile 文件,增加如下内容 JAVA_HOME=/usr/local/jdk1. export JAVA_HOME PA ...

  5. hadoop fs -put localfile . 时出现如下错误: could only be replicated to 0 nodes, instead of 1

    hadoop fs -put localfile . 时出现如下错误:could only be replicated to 0 nodes, instead of 1网友的说法: 这个问题是由于没有 ...

  6. Java 学习笔记(2)——基本语句、控制结构

    上一篇中简单谈了一下自己对Java的一些看法并起了一个头,现在继续总结java的相关语法.java语法总体上与C/C++一样,所以对于一个C/C++程序员来说,天生就能看懂Java代码.在学习java ...

  7. 移动端HTML5实现文件上传

    PC端上传文件多半用插件,引入flash都没关系,但是移动端要是还用各种冗余的插件估计得被喷死,项目里面需要做图片上传的功能,既然H5已经有相关的接口且兼容性良好,当然优先考虑用H5来实现. 用的技术 ...

  8. html技巧

    1.防止盒子透出的解决办法    overflow:hidden:float不为none:display:inline-block:    position不为static&relative  ...

  9. 关于修改bug的思考

     作者:朱金灿 来源:http://blog.csdn.net/clever101 有软件就有bug,这意味着软件研发不仅仅是新功能开发,更要拿出相当一部分精力去修改bug.但基本很多软件开发者并 ...

  10. Linux基础入门之网络属性配置

    Linux基础入门之网络属性配置 摘要 Linux网络属性配置,最根本的就是ip和子网掩码(netmask),子网掩码是用来让本地主机来判断通信目标是否是本地网络内主机的,从而采取不同的通信机制. L ...