Viết class cho editor
Khi nắm được những khái niệm xung quanh thuộc tính designMode và phương thức execCommand, việc xây dựng Rich Text Format Editor không còn là điều khó khăn nữa. Có lẽ những bạn nào mà qua topic này, lần đầu tiên nghe nói đến các khái niệm trên, sẽ có cùng cảm giác như vậy. Hiểu được vấn đề là giải quyết được vấn đề.
Các trình WYSIWYG xây dựng sẵn thường khá cồng kềnh. Chúng càng cố khoác lên mình dáng vẻ của các ứng dụng soạn thảo văn bản desktop thì hình thức càng rườm rà, bề bộn, với những tính năng mà có thể chúng ta không bao giờ dùng đến.
Đây là một số trình soạn thảo WYSIWYG phổ biến :
Khi nắm được những khái niệm xung quanh thuộc tính designMode và phương thức execCommand, việc xây dựng Rich Text Format Editor không còn là điều khó khăn nữa. Có lẽ những bạn nào mà qua topic này, lần đầu tiên nghe nói đến các khái niệm trên, sẽ có cùng cảm giác như vậy. Hiểu được vấn đề là giải quyết được vấn đề.
Các trình WYSIWYG xây dựng sẵn thường khá cồng kềnh. Chúng càng cố khoác lên mình dáng vẻ của các ứng dụng soạn thảo văn bản desktop thì hình thức càng rườm rà, bề bộn, với những tính năng mà có thể chúng ta không bao giờ dùng đến.
Đây là một số trình soạn thảo WYSIWYG phổ biến :
Các Editor cũng giống như mọi ứng dụng nền web, đều có hạn chế là phải phụ thuộc vào mức độ hỗ trợ của các trình duyệt. Một ứng dụng web có thể hoạt động trong nhiều trình duyệt khác nhau, không có lỗi khi chạy, và ít rắc rối, thì dễ gây cảm tình hơn những trò hoa lá.
Về phía những editor mà các dịch vụ web sử dụng thì cái của Gmail hợp với mỹ cảm của tôi hơn cả. Ở đó có sự hài hòa giữa tính đơn giản, vẻ đẹp trang nhã và khả năng hoạt động chuẩn xác, hoàn hảo.
Do bài viết này chỉ nhằm mục đích giới thiệu cách xây dựng trình soạn thảo WYSIWYG, nên chúng ta vẫn giữ nguyên hình thức của bản trước mà không cần thêm các nút lệnh định dạng khác trên toolBar, tránh làm phức tạp vấn đề.
Chúng ta mở đầu bằng đoạn script :
rel trả về phần tử dựa trên id của nó. setHTML có nhiệm vụ gán code HTML vào 1 phần tử nào đó. setBtnAction chỉ định 1 hành động cho 1 button dưới sự kiện click. Hàm này nhận vào 2 tham số : ID của button và chuỗi tên hành động. Ví dụ chúng ta có button btnBold hiện diện dưới dạng 1 icon nhỏ :
Nếu muốn chương trình thực thi lệnh doFomat("bold"), khi click vào hình ảnh này, chúng ta chỉ việc gọi hàm :
Khi chuỗi 'doFormat("bold")' được truyền vào hàm setBtnAction, vì addEventListener và attachEvent bắt buộc tham số thứ 2 phải là 1 hàm, nên setBtnAction sẽ dùng chuỗi này để tạo ra 1 đối tượng Function với toán tử new.
Giữ nguyên các thành phần điều khiển Editor như cũ, chúng ta viết lại code HTML :
Thay vì nhúng các hàm vào từng button, chúng ta đã cho mỗi hình ảnh một giá trị ID.
Nếu hình dung điều khiển WYSIWYG của chúng ta như 1 đối tượng, nó có thể mang các thuộc tính cơ bản như : tên, kích thước vùng làm việc, nội dung bên trong. Và bên cạnh đó là 1 loạt các phương thức thi hành việc định dạng văn bản : chèn link, chèn hình ảnh... Đây là hàm dựng :
Các tham số :
sID : chuỗi tên riêng cho mỗi Editor, bằng cách này, chúng ta có thể sử dụng nhiều Editor trên cùng 1 trang web.
oContain : phần tử chứa Editor mà khung soạn thảo sẽ được tạo ra bên trong.
sDefaultText : văn bản mặc định hiển thị khi Editor mở ra. Đây là 1 chuỗi HTML.
iWidth : chiều rộng của khung soạn thảo.
iHeight : chiều cao của khung soạn thảo.
Trong hàm dựng, chúng ta thiết kế các thuộc tính :
ID : định danh riêng
content : nội dung văn bản trong editor
width : chiều rộng
height : chiều cao
UI : tham chiếu đến khung iframe
editor : tham chiếu đến đối tượng document của khung iframe. Biến này trực tiếp thi hành các command.
Hàm doFormat ở phiên bản cũ sẽ trở thành phương thức formatText của lớp đối tượng RichTextEditor :
Và tương tự cho addLink, insertImage, unformat :
Ngoài ra chúng ta thêm vào 1 phương thức mới là getContent để lấy nội dung văn bản :
Cuối cùng là 1 hàm dùng để khởi tạo trình soạn thảo, được gọi sau khi trang load xuống hoàn chỉnh :
Biến RTE được dùng làm một thể hiện của lớp RichTextEditor. Các dòng sau thiết lập hành động cho từng nút lệnh trên toolBar.
init được gọi trong sự kiện onload :
Và thế là chúng ta đã có phiên bản Rich Text Editor mới, tuy không thay đổi trên giao diện, nhưng cấu trúc chương trình đã trở nên chặt chẽ hơn hẳn.
Từ bản demo rút gọn đến sản phẩm sau cùng chỉ còn một quãng đường ngắn, và chắc hẳn bạn sẽ cảm thấy dễ chịu hơn nếu đi tới đó một mình :)
Về phía những editor mà các dịch vụ web sử dụng thì cái của Gmail hợp với mỹ cảm của tôi hơn cả. Ở đó có sự hài hòa giữa tính đơn giản, vẻ đẹp trang nhã và khả năng hoạt động chuẩn xác, hoàn hảo.
Do bài viết này chỉ nhằm mục đích giới thiệu cách xây dựng trình soạn thảo WYSIWYG, nên chúng ta vẫn giữ nguyên hình thức của bản trước mà không cần thêm các nút lệnh định dạng khác trên toolBar, tránh làm phức tạp vấn đề.
Chúng ta mở đầu bằng đoạn script :
var RTE=null; var isIE=document.all?true:false; var isMz=(navigator.appName=='Netscape')?true:false; function rel(id){ return document.getElementById (id)||null; } function setHTML(el,sHTML){ if(rel(el)){ rel(el).innerHTML=sHTML; } else{ alert('The element "'+el+'" not found !'); } } function setBtnAction(btnID, Action){ var el = rel(btnID); if(el.addEventListener){ el.addEventListener('click', new Function(Action), false); } else if (el.attachEvent){ el.attachEvent('onclick', new Function(Action)); } }
rel trả về phần tử dựa trên id của nó. setHTML có nhiệm vụ gán code HTML vào 1 phần tử nào đó. setBtnAction chỉ định 1 hành động cho 1 button dưới sự kiện click. Hàm này nhận vào 2 tham số : ID của button và chuỗi tên hành động. Ví dụ chúng ta có button btnBold hiện diện dưới dạng 1 icon nhỏ :
<img id="btnBold" src="bold.gif" title="Bold">
Nếu muốn chương trình thực thi lệnh doFomat("bold"), khi click vào hình ảnh này, chúng ta chỉ việc gọi hàm :
setBtnAction('btnBold', 'doFormat("bold")');
Khi chuỗi 'doFormat("bold")' được truyền vào hàm setBtnAction, vì addEventListener và attachEvent bắt buộc tham số thứ 2 phải là 1 hàm, nên setBtnAction sẽ dùng chuỗi này để tạo ra 1 đối tượng Function với toán tử new.
Giữ nguyên các thành phần điều khiển Editor như cũ, chúng ta viết lại code HTML :
<table border="0" cellpadding="0" cellspacing="1" bgcolor="#e1f2ff"> <tr height="20"> <td> &nbsp; <img id="btnBold" src="bold.gif"> <img id="btnItalic" src="italic.gif"> <img id="btnUnderline" src="underline.gif"> <img id="btnInsertImage" src="image.gif"> <img id="btnAddLink" src="link.gif"> <img id="btnUnformat" src="removeformatting.gif"> </td> </tr> <tr> <td align="center" bgcolor="#ffffff"> <span id="wysiwyg"></span> </td> </tr> </table>
Thay vì nhúng các hàm vào từng button, chúng ta đã cho mỗi hình ảnh một giá trị ID.
Nếu hình dung điều khiển WYSIWYG của chúng ta như 1 đối tượng, nó có thể mang các thuộc tính cơ bản như : tên, kích thước vùng làm việc, nội dung bên trong. Và bên cạnh đó là 1 loạt các phương thức thi hành việc định dạng văn bản : chèn link, chèn hình ảnh... Đây là hàm dựng :
function RichTextEditor(sID, oContain, sDefaultText, iWidth, iHeight){ this.ID=sID; this.content=sDefaultText; this.width=(iWidth>300?iWidth:500); this.height=(iHeight>80?iHeight:200); var sEditor='<iframe id="'+this.ID; sEditor+='" style="width:'+this.width; sEditor+='px;height:'+this.height; sEditor+='px;overflow:auto;"></iframe>'; setHTML(oContain,sEditor); this.UI=document.getElementById(this.ID).contentWindow; this.editor=this.UI.document; with(this.editor){ designMode='On'; open(); write('<html><head></head><body>'+this.content+'</body></html>'); close(); } }
Các tham số :
sID : chuỗi tên riêng cho mỗi Editor, bằng cách này, chúng ta có thể sử dụng nhiều Editor trên cùng 1 trang web.
oContain : phần tử chứa Editor mà khung soạn thảo sẽ được tạo ra bên trong.
sDefaultText : văn bản mặc định hiển thị khi Editor mở ra. Đây là 1 chuỗi HTML.
iWidth : chiều rộng của khung soạn thảo.
iHeight : chiều cao của khung soạn thảo.
Trong hàm dựng, chúng ta thiết kế các thuộc tính :
ID : định danh riêng
content : nội dung văn bản trong editor
width : chiều rộng
height : chiều cao
UI : tham chiếu đến khung iframe
editor : tham chiếu đến đối tượng document của khung iframe. Biến này trực tiếp thi hành các command.
Hàm doFormat ở phiên bản cũ sẽ trở thành phương thức formatText của lớp đối tượng RichTextEditor :
RichTextEditor.prototype.formatText=function(command, value){ if(this.editor.queryCommandEnabled(command)){ if(!value){value=null;} this.editor.execCommand(command,false,value); } }
Và tương tự cho addLink, insertImage, unformat :
RichTextEditor.prototype.addLink=function(){ var aLink=prompt('Enter or paste a link :', ''); if(aLink){ this.formatText('CreateLink', aLink); } } RichTextEditor.prototype.insertImage=function(){ this.UI.focus(); var aLink=prompt('Enter or paste a URL :', ''); if(aLink){ this.formatText('InsertImage', aLink); } } RichTextEditor.prototype.unformat=function(){ this.formatText('removeformat'); this.formatText('unlink'); }
Ngoài ra chúng ta thêm vào 1 phương thức mới là getContent để lấy nội dung văn bản :
RichTextEditor.prototype.getContent=function(){ return this.editor.body.innerHTML; }
Cuối cùng là 1 hàm dùng để khởi tạo trình soạn thảo, được gọi sau khi trang load xuống hoàn chỉnh :
function init(){ RTE=new RichTextEditor('RTE', 'wysiwyg', '', 500, 240); setBtnAction('btnBold', 'RTE.formatText("bold")'); setBtnAction('btnItalic', 'RTE.formatText("italic")'); setBtnAction('btnUnderline', ' RTE.formatText("underline");'); setBtnAction('btnInsertImage', 'RTE.insertImage();'); setBtnAction('btnAddLink', 'RTE.addLink();'); setBtnAction('btnUnformat', ' RTE.unformat();'); }
Biến RTE được dùng làm một thể hiện của lớp RichTextEditor. Các dòng sau thiết lập hành động cho từng nút lệnh trên toolBar.
init được gọi trong sự kiện onload :
window.onload=init ;
Và thế là chúng ta đã có phiên bản Rich Text Editor mới, tuy không thay đổi trên giao diện, nhưng cấu trúc chương trình đã trở nên chặt chẽ hơn hẳn.
Từ bản demo rút gọn đến sản phẩm sau cùng chỉ còn một quãng đường ngắn, và chắc hẳn bạn sẽ cảm thấy dễ chịu hơn nếu đi tới đó một mình :)