天天看點

富文本編輯器TinyMCE在vue2.x中的使用以及動态綁定(解決雙向綁定後光标跳到最左側問題)

寫在前面

項目重構老管理背景,使用vue作為前端架構,對于一直使用jsp+jquery開發管理背景的我還是挺頭疼的… 之後在vue官網學習了一下基礎知識外加向前端同僚讨教,粗略了解了vue的使用,如果文中有錯誤地方請多多包涵。

由于不是專業的前端開發,是以并沒有使用

vue-cli

腳手架,隻是用的原生vue。

下面是最終效果圖:

富文本編輯器TinyMCE在vue2.x中的使用以及動态綁定(解決雙向綁定後光标跳到最左側問題)

引入vue和tinyMCE

tinyMCE下載下傳位址:https://www.tiny.cloud/get-tiny/self-hosted/

其中tinyMCE預設為英文,如需中文需要下載下傳語言擴充包,下載下傳位址:https://www.tiny.cloud/get-tiny/language-packages/ 。将

zh_CN.js

檔案複制到

langs

檔案夾下

tinyMCE內建了絕大多數功能,但是調整行高的功能并沒有,我們需要額外下載下傳該插件。忘記之前在哪裡找到的了,直接放上行高插件,下載下傳位址:tinyMCE富文本編輯器line-height行高插件

該插件預設是德語,我們可以直接修改

plugin.min.js

檔案源碼改為中文提示,找到

return{type

開頭的内容,修改為

return{type:"listbox",text:"行間距",tooltip:"行間距",...

初始化tinyMCE

工具欄的配置可以參考這篇文章,介紹的很詳細:TinyMCE工具欄配置詳解

代碼如下:

其中

valid_elements

valid_style

是将合法标簽以及屬性加入白名單的,我這裡寫的是根據策劃要求來的,因人而異。官方文檔說明:valid_elements,valid_style

tinymce.init({
            selector: '#mytextarea',
            language: 'zh_CN',
            menubar: false,
            valid_elements :"p[style],span[style],ul,ol,li,strong/b,em,h1,h2,h3,h4,h5,h6",
            valid_style:{
            	"*":"color,font_size,text-align,line-height"
            },
            plugins:"code,textcolor,lists,lineheight,fullscreen",
            toolbar: [
                      'undo redo | formatselect | bold italic forecolor fontsizeselect | alignleft aligncenter alignright | bullist numlist lineheightselect | code fullscreen | removeformat '
                    ]
        });
           

之後在html裡引入這個textarea就可以了

其中通過

tinyMCE.activeEditor.getContent()

以及

tinyMCE.activeEditor.setContent(val)

可以手動擷取、設定tinyMCE的值

其中需要注意的小細節:

toolbar

中的

|

是用來分割工具欄的,

|

和工具欄的名稱中間必須有

空格

,否則無法識别該按鈕。

<textarea>

外面一定要用

<div>

套一層,否則無法正常顯示

元件化tinyMCE并設定雙向綁定

為了友善使用,我們使用vue component将tinyMCE元件化。同時為了滿足實時監控富文本内容的需求,不能采用最後在ajax方法中手動get/set内容的政策了,需要用v-model實時雙向綁定。

定義傳入的字段

props: ['value']

,用來接收父元件的值,當編輯器檢測到有變化時調用$emit(‘input’, value)将資料寫出到父元件

具體代碼如下:

Vue.component('tinymce', {
    props: ['value'],
    watch:{
        value(val){
            tinyMCE.activeEditor.setContent(val);
        }
    },
    mounted: function(){
        var component = this;
        tinymce.init({
            target: this.$el.children[0],
            language: "zh_CN",
            menubar: false,
            branding: false,
            valid_elements: "p[style],span[style],ul,ol,li,strong/b,em,h1,h2,h3,h4,h5,h6",
            valid_style: {
                "*":"color,font_size,text-align,line-height"
            },
            plugins: "code,textcolor,lists,lineheight,fullscreen",
            toolbar: [
                'undo redo | formatselect | bold italic forecolor fontsizeselect | alignleft aligncenter alignright | bullist numlist lineheightselect | code fullscreen | removeformat '
            ],
            setup: function(editor) {
                editor.on('input change undo redo execCommand KeyUp', function(e) {
                    component.$emit('input', editor.getContent());
                }) 
            }
        });
    },
  template: `<div><textarea style="height:300px" v-model="value"></textarea></div>`
});
           

解決光标移動問題(10.19更新,解決中文輸入法無法輸入問題)

雖然我們已經做到了tinyMCE展示并且可以雙向綁定資料,但是輸入的時候會發現光标一直回到最左側。這是因為我們輸入文字時觸發

$emit('input', editor.getContent())

修改了value,這樣就又會進入

watch

,進而再次調用

tinyMCE.activeEditor.setContent(val)

。這裡我參考了前端手劄——vue元件vue-tinymce開發經驗分享這篇文章,文中采用了

這裡解決辦法是目前編輯不讓觸發editor.seContent()就不會導緻光标更新

。作者定義了一個

status

用來記錄不同狀态,但是我這裡測試後不可以-.-,同時作者還提出了一種思路:

(當然還有其他方法,比如記錄光标位置)

。于是我就按照這種思路去解決光标跳動問題,感覺更簡單一些,供大家參考。

我們查閱tinyMCE的api文檔,發現有一個BookmarkManager,它可以記錄位置并移動。

getBookmark(type:Number, normalized:Boolean):Object

Returns a bookmark location for the current selection. This bookmark object can then be used to restore the selection after some content modification to the document.

moveToBookmark(bookmark:Object):Boolean

Restores the selection to the specified bookmark.

其中

getBookmark

的兩個參數官方文檔中并沒有具體說明,但是經過試驗,第一個參數必須使用

2

才生效。其實也是google tinymce cursor jump 然後在stackoverflow上偶爾看見的,大家有興趣可以具體了解下Preserve caret/bookmark position in tiny while using setContent

var bm=tinyMCE.activeEditor.selection.getBookmark(2);
tinyMCE.activeEditor.setContent(val);
tinyMCE.activeEditor.selection.moveToBookmark(bm);
           

這樣整個tinyMCE的建構以及資料雙向綁定就完成了,最後在需要使用tinyMCE的頁面調用該元件就可以了。

10.19更新

之前考慮的采用bookmark防止光标移動政策還是會有問題,因為每次修改編輯器的值其實還是會觸發

watch

,進而調用

tinyMCE.activeEditor.setContent(val)

,我隻是在前後記錄、移動了光标位置,保證輸入時光标不會跳到最左側。但在chrome浏覽器下,輸入中文時拼音會先填充在文本框裡,如下圖:

富文本編輯器TinyMCE在vue2.x中的使用以及動态綁定(解決雙向綁定後光标跳到最左側問題)

這樣就會觸發

input

進而觸發

watch

裡的

tinyMCE.activeEditor.setContent(val)

,導緻輸入法消失。是以這樣來看,我們在編輯器裡操作時是就是不應該去觸發

tinyMCE.activeEditor.setContent(val)

我最終的做法是加入一個

flag

标志,在編輯器觸發

input undo redo execCommand

事件時将

flag

置為

false

watch

的時候隻有

flag

true

時才調用

tinyMCE.activeEditor.setContent(val)

,并且最終都置回

true

ps:後來決定不監聽

change

是因為

change

事件需要失去焦點并且文本變化時觸發,這樣會導緻先觸發

input

再觸發

change

,而由于資料已經在

input

事件中修改,是以

change

不會調用

watch

重新把

flag

置為

true

,是以這時候的

flag

就定格在了

false

這個方法應該也不是太好,但是我不能在這一個功能上再耗太多時間了,就先這樣處理了,基本滿足要求了。

完整代碼

  • tinyMCE元件js
Vue.component('tinymce', {
    props: ['value'],
    data(){
        return{
            flag:true
        }
    },
    watch:{
        value(val){
            if(this.flag){
                tinyMCE.activeEditor.setContent(val);
            }
            this.flag=true;
        }
    },
    mounted: function(){
        var component = this;
        tinymce.init({
            target: this.$el.children[0],
            language: "zh_CN",
            menubar: false,
            branding: false,
            valid_elements: "p[style],span[style],ul,ol,li,strong/b,em,h1,h2,h3,h4,h5,h6",
            valid_style: {
                "*":"color,font_size,text-align,line-height"
            },
            plugins: "code,textcolor,lists,lineheight,fullscreen",
            toolbar: [
                'undo redo | formatselect | bold italic forecolor fontsizeselect | alignleft aligncenter alignright | bullist numlist lineheightselect | code fullscreen | removeformat '
            ],
            setup: function(editor) {
                editor.on('input undo redo execCommand', function(e) {
                    component.flag=false;
                    component.$emit('input', editor.getContent());
                }) 
            }
        });
    },
  template: `<div><textarea style="height:300px" v-model="value"></textarea></div>`
});
           
  • 需要引入tinyMCE的頁面
......


<body>

<div id="test">
<tinymce v-model="content"></tinymce>
</div>

<!--vue-->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<!--下載下傳的tinyMCE-->
<script src="../plugins/tinymce/tinymce.min.js"></script>
<!--tinyMCE元件js-->
<script src="../js/tinymceTemplate.js"></script>
<script>

    new Vue({
        el:"#test",
        data(){
            return{
                content:""
            }
        },
        mounted:function(){
            //TODO
        },
        methods:{
        	//TODO
        }
    });

</script>

</body>
           

寫在最後

ok 大功告成!

對于一個後端開發接觸vue以及一些插件的使用還是很有必要的,用了才發現比以前用jsp jquery友善多了,真香!不過畢竟還是小白,用的也是非常的基礎,還需不斷努力。

ps:使用vue時發現有一款內建在chrome上的vue開發插件,可以實時監測vue中的資料,并且可以手動修改,很友善。附上連結:https://github.com/vuejs/vue-devtools