這個原本是給 vm-manager 寫的一個富文本編輯器,後來以爲獨立出來維護比較方便,就單獨分離出來放到NPM。之因此說人人都會寫, 是由於這個組件實現起來確實比較簡單,不須要很厲害的Js水平, 基本是對document.execCommand()指令的綁定。在此將過程分享給你們html
預覽地址 https://luosijie.github.io/vm-editor/vue
源碼地址 https://github.com/luosijie/vm-editorgit
npm install --save vm-editor
//upload綁定事件將編譯的html字符傳給父組件 <VmEditor @upload="showHtml"></VmEditor> <script> import VmEditor from 'vm-editor' export default { ... methods: { showHtml: function(data){ console.log(data) } } } </script>
由於是Vue組件, 因此寫這樣的一個組件,須要掌握的知識點有:github
組件由 菜單部分和內容區域 2大部分組成, 其中菜單區域又由各類 指令按鈕 組成,部分指令按鈕還有下拉選項npm
指令按鈕是 execConmand 的裝載器,須要實現如下功能瀏覽器
<template> <button class="vm-editor-button" :class="{ active: slot }"> // 顯示按鈕圖標 <img :src="require('../assets/iconimg/' + icon + '.png')" height="16" width="16" alt="" @click="showSlot"> <!-- <i :class="icon" @click="showSlot"></i> --> // 部分按鈕須要實現點擊展開下拉菜單 <slot v-if="slot"></slot> </button> </template> <style> ... // 劃過顯示背景 button.vm-editor-button:hover{ background-color: #eee; } ... </style> export default { name: 'VmEditorButton', props: { icon: { type: String, default: 'heading' } }, data: function () { return { slot: false } }, methods: { showSlot () { this.slot === false ? this.slot = true : this.slot = false } } } </script>
菜單區域放置按鈕,主要實現的功能是app
<template> <div class="vm-editor-menu"> // 引入按鈕組建, 經過 **click事件** 綁定封裝的 **execCommand方法** 實現樣式的變化 <VmEditorButton icon="paragraph" @click.native="execCommand('formatBlock', '<p>')"> </VmEditorButton> <VmEditorButton icon="heading"> <VmEditorDropdown> // 這是部分按鈕須要下拉菜單功能 <ul class="vm-editor-ul"> <li @click="execCommand('formatBlock', '<h1>')"> <h1>H1</h1> </li> <li @click="execCommand('formatBlock', '<h2>')"> <h2>H2</h2> </li> <li @click="execCommand('formatBlock', '<h3>')"> <h3>H3</h3> </li> <li @click="execCommand('formatBlock', '<h4>')"> <h4>H4</h4> </li> <li @click="execCommand('formatBlock', '<h5>')"> <h5>H5</h5> </li> </ul> </VmEditorDropdown> </VmEditorButton> // 省略其餘按鈕代碼 ... <slot></slot> </div> </template> <style> ... </style> <script> ... export default { name: 'VmEditorMenu', components: { VmEditorButton, VmEditorDropdown, VmEditorAddlink, VmEditorAddimage, VmEditorFontcolor }, methods: { // 封裝 document.execCommand 指令 execCommand: function (commandName, valueArgument) { // let body = document.querySelector('.body'); if (!valueArgument) { valueArgument = null } document.execCommand('styleWithCSS', null, true) document.execCommand(commandName, false, valueArgument) }, // 插入圖片功能 setImage: function (evt) { let reader = new FileReader() let file = evt.target.files[0] reader.readAsDataURL(file) reader.onload = function (evt) { let base64Image = evt.target.result document.execCommand('insertImage', false, base64Image) } } } } </script>
主組件就是將 菜單組件和 內容區域 整合在一塊兒編輯器
另外還要實現導出html的功能ide
<div class="vm-editor"> <VmEditorMenu> <div class="global-control"> // 處處html的按鈕,放在這裏由於,須要獲取 內容區域 的html數據 <VmEditorButton icon="upload" @click.native="uploadHtml"></VmEditorButton> </div> </VmEditorMenu> // 內容區域 只要設置 **contenteditable="true"** 就能夠了,其餘的交給指令去作 <div class="content" contenteditable="true" v-html="html"> </div> </div> <style> ... </style> <script> name: 'VmEditor', components: { VmEditorMenu, VmEditorButton }, data: function () { return { html: 'Please Enter ...' } }, methods: { 導出html數據 // 目前 內容區域 的樣式都是 **CSS樣式**, 導出時須要轉化爲 **內聯樣式** uploadHtml: function () { // 獲取各個模塊的 CSS樣式 let style = { ul: ` margin: 10px 20px; list-style-type: square; padding: 0; `, ol: ` margin: 10px 20px; list-style-type: decimal; padding: 0; `, li: ` display: list-item; padding: 0; `, hr: ` margin: 15px 0; border-top: 1px solid #eeeff1; `, pre: ` display: block; margin: 10px 0; padding: 8px; border-radius: 4px; background-color: #f2f2f2; color: #656565; font-size: 14px; `, blockquote: ` display: block; border-left: 4px solid #ddd; margin: 15px 0; padding: 0 15px; `, img: ` margin: 20px 0; `, a: ` color: #41b883; ` } let html = document.getElementsByClassName('content')[0] let htmlContainerParent = document.createElement('div') let htmlContainer = document.createElement('div') let tagNames = Object.keys(style) // 遍歷html節點並插入對應的內聯樣式 for (let i = 0; i < tagNames.length; i++) { let _tagNames = html.getElementsByTagName(tagNames[i]) if (_tagNames.length > 0) { for (let j = 0; j < _tagNames.length; j++) { _tagNames[j].style = style[tagNames[i]] } } } htmlContainer.style = ` text-align: left; padding: 15px; font-size: 16px; ` htmlContainer.innerHTML = html.innerHTML htmlContainerParent.appendChild(htmlContainer) // 註冊自定義事件 **upload** this.$emit('upload', htmlContainerParent.innerHTML) } } } </script>
其餘的組建主要是按鈕下拉菜單, 由於每一個都不同,因此要獨立出來學習
由於這個富文本編輯器的開發時間比較短,沒有認真研究相似優秀插件的源碼 也沒有 深刻調研過富文本編輯器的需求。
只是參考了一些同類編輯器的實現效果和UI風格,好比simditor,而後簡單實現了一下功能。
因此確定還有不少須要改善的地方,比較明顯的有:
無論怎樣,僅供你們學習使用
先這樣了, 歡迎star