2016年1月15日 星期五

如何在網頁中使用網頁編輯器 TinyMCE

去年底因應 IE11 升版將工作日誌的 HTML 編輯器換新為 CKEditor 後, 最近同事反映此編輯器用起來沒有舊的 FCKeditor 好用, 我原先覺得那只是習慣問題吧! 我自己用起來還 OK 啊! 那是我花了一個禮拜才搞定改版的種種問題耶! 參考 :

# 如何在網頁中使用網頁編輯器 CKEDITOR

但使用者的體驗是真實的, 不應該漠視. 所以今天看到下列這篇, 介紹十種最好用的所見即所得 HTML 編輯器後, 發現其中的 TinyMCE 似乎不錯 : 


其展示範例見 :

https://www.tinymce.com/docs/demo/full-featured/ (全功能)

TinyMCE 是可免費使用的 Open Source 軟體, 但也提供收費升級版. 可下載 zip 檔解壓縮後放在專案目錄下 :

https://www.tinymce.com/download/

目前是 4.3.3 版, 解壓縮後會產生 tinymce/js/tinymce 的目錄結構, 只要將 js 底下的那個 tinymce 目錄放到專案目錄下即可, 我的工作日誌系統是把所有外掛放在 /plug-in 下面, 結構如下 :


裡面的 example.htm 是我自己寫的範例, 不是原 zip 檔中的內容.

TinyMCE 的用法與 CKEditor 類似, 都是利用 textarea 包裝成網頁編輯器物件 tinymce (或 tinyMCE). 只要匯入 TinyMCE 函式庫, 然後呼叫 tinymce 物件的 init() 方法, 並指定要初始化的 textarea 元件即可 :

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>TinyMCE 測試</title>
    <script src="tinymce.min.js"></script>
  </head>
  <body>
    <form>
      <textarea id="editor1">
      Hello World! 這是 TinyMCE!
      </textarea>
    </form>
    <script>
      tinymce.init({
        selector:'textarea'
        });
    </script>
  </body>
</html>

此處利用 selector 屬性選取 textarea 元件來初始化. 如果網頁中有多個 textarea, 那此種選取方法會將全部 textarea 元件都初始化. 也可以用 id 來選取 (CSS 用 # 表示選取 id) :

      tinymce.init({
        selector:'#editor1'
        });

這樣當兩個編輯器要做不同處理時 (例如設定不同之內容), 就要用 id 選取來個別初始化. 與 CKEditor 不同的是, 它上面多了功能選單 :

範例 1 : http://mybidrobot.allalla.com/tinymce/tinymce_1.htm


預設介面是英文版的, 如果要改為中文版, 必須另外去 TinyMCE 的Archieve 網站下載語言檔 (zip) :  

http://archive.tinymce.com/i18n/download.php?download=zh_TW

解開後會得到內含 zh_TW.js 檔的 langs 目錄, 將其複製到 tinymce 目錄下覆蓋原目錄 (裡面預設無語言檔), 然後在初始化時加入 language 屬性, 指定其值為 "zh_TW" 即可 :

      tinymce.init({
        selector:'#editor1',
        language:'zh_TW'
        });

範例 2 : http://mybidrobot.allalla.com/tinymce/tinymce_2.htm


除了自備資源外, 如果專案是放在 Internet 上, 則利用 CDN 會比較方便, 不須準備檔案, 只要將 TinyMCE 函式庫指向下列網址之一即可 :

http://cdn.tinymce.com/4/tinymce.min.js
https://cdnjs.cloudflare.com/ajax/libs/tinymce/4.3.3/tinymce.min.js

但是 CDN 不提供語言檔, 只能用預設之英文版, 不可以使用 language 屬性去設定, 否則會因為找不到語言檔, 導致網頁無法渲染而發生錯誤. 如果使用 CDN 又要 Localization, 必須自行提供語言檔, 並用 language_url 屬性指定其位址, 如下例所示 :

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>TinyMCE 測試</title>
    <script src="http://cdn.tinymce.com/4/tinymce.min.js"></script>
  </head>
  <body>
    <form>
      <textarea id="editor1">
      Hello World! 這是 TinyMCE!
      </textarea>
    </form>
    <script>
      tinymce.init({
        selector:'#editor1',
        language_url:'http://mybidrobot.allalla.com/tinymce/langs/zh_TW.js'
        });
    </script>
  </body>
</html>

範例 3 : http://mybidrobot.allalla.com/tinymce/tinymce_3.htm

除了繁體中文外, 我也下載了日, 韓, 簡體中文語言檔放在 langs 下, 例如使用韓文介面就把上面的 zh_TW.js 改為 ko_KR.js 即可 (但我這免費的測試網站並不穩定, 有時會無法存取).

範例 4 : http://mybidrobot.allalla.com/tinymce/tinymce_4.htm (日文)
範例 5 : http://mybidrobot.allalla.com/tinymce/tinymce_5.htm (韓文)
範例 6 : http://mybidrobot.allalla.com/tinymce/tinymce_6.htm (簡體中文)
範例 7 : http://mybidrobot.allalla.com/tinymce/tinymce_7.htm (阿拉伯文)
範例 8 : http://mybidrobot.allalla.com/tinymce/tinymce_8.htm (俄文)






預設的工具列與功能表乃最簡設定, TinyMCE 提供了 plugins 與 toobar 屬性來自定工具列, 其值為一個由工具按鈕名稱組成的陣列. 工具列上的按鈕分屬於不同的 plugins, 參考 :

# Buttons/controls

因此如果要擺上某個按鈕, 那麼其所屬的 plugins 也要列在 plugins 陣列內, 否則會沒效果. 花了一晚上測試工具列的組合, 終於得到比較滿意的排列, 如範例 9 所示 :

      tinymce.init({
        selector:'#editor1',
        language_url:'http://mybidrobot.allalla.com/tinymce/langs/zh_TW.js',
        height:300,
        width:800,
        plugins:[
          'advlist autolink lists link image charmap print preview anchor',
          'searchreplace visualblocks code fullscreen textcolor colorpicker',
          'insertdatetime media table contextmenu paste code hr pagebreak nonbreaking'
          ],
        toolbar:['newdocument preview fullscreen code print searchreplace selectall | bold italic underline strikethrough superscript subscript removeformat forecolor backcolor | alignleft aligncenter alignright alignjustify |',
        'undo redo cut copy paste pastetext pasteword | bullist numlist outdent indent | blockquote nonbreaking hr pagebreak charmap anchor link unlink image table']
        });

範例 9 : http://mybidrobot.allalla.com/tinymce/tinymce_9.htm


這裡我們也使用 width 與 height 設定編輯器的大小, 當然也可以在 textarea 加上 css 樣式來設定. 在 toolbar 屬性值陣列中, 管線符號用來分群, 逗號則用來跳行. 注意, 管線符號與工具按鈕名稱之間必須用空格隔開, 如果黏在一起會出現錯誤. 其次, 搜尋鈕右邊的全選 (selectall) 按鈕圖像不知何故沒有顯現, 但其實是有作用的.

如果不需要功能選項, 可以將 menubar 屬性設為 false 即可, 這樣如下範例 10 所示 :

      tinymce.init({
        selector:'#editor1',
        language_url:'http://mybidrobot.allalla.com/tinymce/langs/zh_TW.js',
        height:300,
        width:800,
        menubar:false,
        plugins:[
          'advlist autolink lists link image charmap print preview anchor',
          'searchreplace visualblocks code fullscreen textcolor colorpicker',
          'insertdatetime media table contextmenu paste code hr pagebreak nonbreaking'
          ],
        toolbar:['newdocument preview fullscreen code print searchreplace selectall | bold italic underline strikethrough superscript subscript removeformat forecolor backcolor | alignleft aligncenter alignright alignjustify |',
        'undo redo cut copy paste pastetext pasteword | bullist numlist outdent indent | blockquote nonbreaking hr pagebreak charmap anchor link unlink image table']
        });

範例 10 : http://mybidrobot.allalla.com/tinymce/tinymce_10.htm


接下來是重頭戲, 要測試 TinyMCE 的設值 setContent() 與取值方法 getContent(), 這是網頁專案中一定會用到的, 例如編輯工作日誌中的紀錄時, 要從資料庫中取出欄位值, 將其設值給 TinyMCE 編輯器; 而要存回資料庫時, 須從 TinyMCE 編輯器中取值, 再回傳給後端處理. 參考 :

http://archive.tinymce.com/wiki.php/API3:method.tinymce.Editor.getContent
http://archive.tinymce.com/wiki.php/API3:method.tinymce.Editor.setContent

不過 setContent() 與 getContent() 這兩個方法不能直接在 init() 後使用, 否則會出現 "Uncaught TypeError: Cannot read property 'getContent' of null" 錯誤, 如下面範例 11 所示 :

範例 11 : http://mybidrobot.allalla.com/tinymce/tinymce_11.htm

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>TinyMCE 測試</title>
    <script src="http://cdn.tinymce.com/4/tinymce.min.js"></script>
  </head>
  <body>
    <form>
      <textarea id="editor1">TinyMCE</textarea>
    </form>
    <script type="text/javascript">
      tinyMCE.init({
        selector:'#editor1'
        });
      alert(tinyMCE.get("editor1").getContent());
      tinyMCE.get("editor1").setContent('Hello World! 這是 TinyMCE!');
    </script>
  </body>
</html>

必須放在方法中, 然後用個按鈕來呼叫它才可以, 如下範例 12 :

範例 12 : http://mybidrobot.allalla.com/tinymce/tinymce_12.htm

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>TinyMCE 測試</title>
    <script src="http://cdn.tinymce.com/4/tinymce.min.js"></script>
  </head>
  <body>
    <form>
      <textarea id="editor1">TinyMCE</textarea>
      <button onclick="get_content()">Get content</button>
      <button onclick="set_content()">Set content</button>
    </form>
    <script>
      tinyMCE.init({
        selector:'#editor1'
        });
      function get_content(){
        var content=tinymce.get("editor1").getContent();  //以 id 取得物件
        alert(content);
        }
      function set_content(){
        tinymce.get("editor1").setContent('Hello World! 這是 TinyMCE!');
        }
    </script>
  </body>
</html>

但是這個範例有一個問題, 就是設值後會重新 init 編輯器, 導致所設的值像曇花一現一下子又變回預設值了. 原因是我們有用 form 元素之故, 當按了按鈕後會向後端提交, 但沒有指定 action, 這樣會向本頁提交, 結果就重新載入本網頁, 編輯器內容當然就重設為預設值了 :

範例 13 : http://mybidrobot.allalla.com/tinymce/tinymce_13.htm

這樣就能單純地驗證 getContent() 與 setContent() 方法了. 當然, 在與後端正常的互動中一定要有 form 表單元素才行, 我們可以在按下確定鈕後呼叫一個方法, 再用 getContent() 取得編輯器內容, 然後再呼叫 submit() 提交表單, 如下範例 14 所示 :

範例 14 : http://mybidrobot.allalla.com/tinymce/tinymce_14.htm

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>TinyMCE 測試</title>
    <script src="http://cdn.tinymce.com/4/tinymce.min.js"></script>
  </head>
  <body>
    <form method="post" action="get_content.php">
      <textarea id="editor1" name="editor1">TinyMCE</textarea>
      <button onclick="check(this.form)">Submit</button>
      <input type="hidden" name="content">
    </form>
    <script>
      tinyMCE.init({      
        selector:'#editor1',
        auto_focus:'editor1'
        });
      function check(formObj){
        var content=tinymce.get("editor1").getContent();
        content="編輯的內容 : <br>" + content;
        formObj.content.value=content;
        alert(content);
        formObj.submit();
        }
    </script>
  </body>
</html>

在這個範例中, 我在表單中增加一個名為 content 的隱藏元件, 當按下 Submit 按鈕時會先呼叫 check() 方法, 並傳入表單物件 this.form, 然後在 check() 中用 getContent() 取得編輯器內容, 在其前冠上額外資訊後, 設值給隱藏元件 content 後向後端 get_content.php 以 post 方法提交表單, 此後端程式很簡單, 就單純取得所傳遞的 content 參數後輸出給前端而已 :

<?php
header('Content-Type: text/html;charset=UTF-8');
echo $_POST['content'];
?>



注意, 這裡我們在初始化編輯器時, 加入了一個新屬性 auto_focus, 其值為編輯器的 id 屬性值 (但不可加 #), 這可以讓網頁載入時讓編輯器自動取得焦點 (也就是游標會在編輯區內一閃一閃的), 參考 :

https://www.tinymce.com/docs/configure/integration-and-setup/#auto_focus
http://fiddle.tinymce.com/
http://stackoverflow.com/questions/31475325/tinymce-get-content
http://fiddle.tinymce.com/WSeaab/1

其次, 編輯器物件不管是用 tinymce 或 tinyMCE 都可以, 都是別名.

以上測試範例檔可從下面網址下載 :

下載測試範例

其他參考資料 :

How do I set content in TinyMCE 4, preferably within the $(document).ready(); block?
# how to enable font family and color options in tinymce editor?
https://www.tinymce.com/docs/plugins/colorpicker/
# TinyMCE width and height disobedient!


1 則留言 :

茉莉 提到...

請問您能結合ibrowser圖片上傳功能嗎?
能將圖,文寫入資料庫嗎? 若寫一有密碼登錄的後台,登陸後有發圖文功能, 這樣價格如何?
請與我聯繫好嗎? 謝謝.