包含修改字体,图片上传等功能的文本输入框-Bootstrap

通过jQuery Bootstrap小插件,框任何一个div转换变成一个富文本编辑框,主要特色:

  • 在Mac和window平台下自动针对常用操作绑定热键
  • 可以拖拽插入图片,支持图片上传(也可以获取移动设备上的照片)
  • 依赖于浏览器标准,没有标准代码;工具条和键盘均可定制,并且能够执行任何浏览器支持的命令

首先看一下效果:

接下来,上代码:

 

 1 <!DOCTYPE html>
 2 <html lang="en">
 3  <head>
 4     <meta charset="utf-8">
 5     <title>Hello, Bootstrap</title>
 6     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
 7     <meta name="keywords" content="opensource rich wysiwyg text editor jquery bootstrap execCommand html5" />
 8     <meta name="description" content="This tiny jQuery Bootstrap WYSIWYG plugin turns any DIV into a HTML5 rich text editor" />
 9     <link rel="shortcut icon" href="http://mindmup.s3.amazonaws.com/lib/img/favicon.ico" >
10     <link href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/css/bootstrap-combined.no-icons.min.css" rel="stylesheet">
11     <link href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/css/bootstrap-responsive.min.css" rel="stylesheet">
12         <link href="http://netdna.bootstrapcdn.com/font-awesome/3.0.2/css/font-awesome.css" rel="stylesheet">
13     <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
14     
15         <script src="external/jquery.hotkeys.js"></script>
16     <script src="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/js/bootstrap.min.js"></script>
17     <script src="external/google-code-prettify/prettify.js"></script>
18         <link href="index.css" rel="stylesheet">
19     <script src="bootstrap-my.js"></script>
20     <script src="indexexercise.js" ></script>
21   </head>
22   <body>
23 
24 <div class="container"style="width:651px;">
25   <div class="hero-unit" style="width: 600px; padding: 8px 30px 30px 30px;">
26     <div id="alerts"></div>
27     <div class="btn-toolbar" data-role="editor-toolbar" data-target="#editor">
28       <div class="btn-group">
29         <a class="btn dropdown-toggle" data-toggle="dropdown" title="Font"><i class="icon-font"></i><b class="caret"></b></a>
30           <ul class="dropdown-menu">
31           </ul>
32         </div>
33       <div class="btn-group">
34         <a class="btn dropdown-toggle" data-toggle="dropdown" title="Font Size"><i class="icon-text-height"></i>&nbsp;<b class="caret"></b></a>
35           <ul class="dropdown-menu">
36           <li><a data-edit="fontSize 5"><font size="5">Huge</font></a></li>
37           <li><a data-edit="fontSize 3"><font size="3">Normal</font></a></li>
38           <li><a data-edit="fontSize 1"><font size="1">Small</font></a></li>
39           </ul>
40       </div>
41       <div class="btn-group">
42         <a class="btn" data-edit="insertunorderedlist" title="Bullet list"><i class="icon-list-ul"></i></a>
43         <a class="btn" data-edit="insertorderedlist" title="Number list"><i class="icon-list-ol"></i></a>
44       </div>
45       <div class="btn-group">
46         <a class="btn" title="Insert picture (or just drag & drop)" id="pictureBtn"><i class="icon-picture"></i></a>
47         <input type="file" data-role="magic-overlay" data-target="#pictureBtn" data-edit="insertImage" />
48       </div>
49       <input type="text" data-edit="inserttext" id="voiceBtn" x-webkit-speech="">
50     </div>
51 
52     <div id="editor" style="width: 568px;height: 145px;">
53      welcome to edit in your space &hellip;
54     </div>
55   </div>
56 </div>
57 </html>

 

对应的indexexercise.js部分:

 1 jQuery(document).ready(function(){
 2   
 3   $(function(){
 4     function initToolbarBootstrapBindings() {
 5       var fonts = ['Serif', 'Sans', 'Arial', 'Arial Black', 'Courier', 
 6             'Courier New', 'Comic Sans MS', 'Helvetica', 'Impact', 'Lucida Grande', 'Lucida Sans', 'Tahoma', 'Times',
 7             'Times New Roman', 'Verdana'],
 8             fontTarget = $('[title=Font]').siblings('.dropdown-menu');
 9       $.each(fonts, function (idx, fontName) {
10           fontTarget.append($('<li><a data-edit="fontName ' + fontName +'" style="font-family:\''+ fontName +'\'">'+fontName + '</a></li>'));
11       });
12       $('a[title]').tooltip({container:'body'});
13         $('.dropdown-menu input').click(function() {return false;})
14             .change(function () {$(this).parent('.dropdown-menu').siblings('.dropdown-toggle').dropdown('toggle');})
15         .keydown('esc', function () {this.value='';$(this).change();});
16 
17       $('[data-role=magic-overlay]').each(function () { 
18         var overlay = $(this), target = $(overlay.data('target')); 
19         overlay.css('opacity', 0).css('position', 'absolute').offset(target.offset()).width(target.outerWidth()).height(target.outerHeight());
20       });
21       if ("onwebkitspeechchange"  in document.createElement("input")) {
22         var editorOffset = $('#editor').offset();
23         $('#voiceBtn').css('position','absolute').offset({top: editorOffset.top, left: editorOffset.left+$('#editor').innerWidth()-35});
24       } else {
25         $('#voiceBtn').hide();
26       }
27     };
28     function showErrorAlert (reason, detail) {
29         var msg='';
30         if (reason==='unsupported-file-type') { msg = "Unsupported format " +detail; }
31         else {
32             console.log("error uploading file", reason, detail);
33         }
34         $('<div class="alert"> <button type="button" class="close" data-dismiss="alert">&times;</button>'+ 
35          '<strong>File upload error</strong> '+msg+' </div>').prependTo('#alerts');
36     };
37     initToolbarBootstrapBindings();  
38     $('#editor').wysiwyg({ fileUploadError: showErrorAlert} );
39     window.prettyPrint && prettyPrint();
40   });
41   
42   (function(i,s,o,g,r,a,m)
43   {i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
44   (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
45   m=s.getElementsByTagName(o)[0];a.async=1;
46   a.src=g;m.parentNode.insertBefore(a,m)})
47 (window,document,'script','//www.google-analytics.com/analytics.js','ga');
48   ga('create', 'UA-37452180-6', 'github.io');
49   ga('send', 'pageview');
50    
51    (function(d, s, id) {
52   var js, fjs = d.getElementsByTagName(s)[0];
53   if (d.getElementById(id)) 
54        return;
55   js = d.createElement(s); 
56   js.id = id;
57   js.src = "http://connect.facebook.net/en_GB/all.js#xfbml=1";
58   fjs.parentNode.insertBefore(js, fjs);
59   }(document, 'script', 'facebook-jssdk'));
60   
61   !function(d,s,id){
62     var js,fjs=d.getElementsByTagName(s)[0];
63     if(!d.getElementById(id)){
64      js=d.createElement(s);
65      js.id=id;
66      js.src="http://platform.twitter.com/widgets.js";
67      fjs.parentNode.insertBefore(js,fjs);}}
68      (document,"script","twitter-wjs"); 
69 })

 对应的bootstrap-my.js部分:

  1 (function ($) {
  2     'use strict';
  3     var readFileIntoDataUrl = function (fileInfo) {
  4         var loader = $.Deferred(),
  5             fReader = new FileReader();
  6         fReader.onload = function (e) {
  7             loader.resolve(e.target.result);
  8         };
  9         fReader.onerror = loader.reject;
 10         fReader.onprogress = loader.notify;
 11         fReader.readAsDataURL(fileInfo);
 12         return loader.promise();
 13     };
 14     $.fn.cleanHtml = function () {
 15         var html = $(this).html();
 16         return html && html.replace(/(<br>|\s|<div><br><\/div>|&nbsp;)*$/, '');
 17     };
 18     $.fn.wysiwyg = function (userOptions) {
 19         var editor = this,
 20             selectedRange,
 21             options,
 22             toolbarBtnSelector,
 23             updateToolbar = function () {
 24                 if (options.activeToolbarClass) {
 25                     $(options.toolbarSelector).find(toolbarBtnSelector).each(function () {
 26                         var command = $(this).data(options.commandRole);
 27                         if (document.queryCommandState(command)) {
 28                             $(this).addClass(options.activeToolbarClass);
 29                         } else {
 30                             $(this).removeClass(options.activeToolbarClass);
 31                         }
 32                     });
 33                 }
 34             },
 35             execCommand = function (commandWithArgs, valueArg) {
 36                 var commandArr = commandWithArgs.split(' '),
 37                     command = commandArr.shift(),
 38                     args = commandArr.join(' ') + (valueArg || '');
 39                 document.execCommand(command, 0, args);
 40                 updateToolbar();
 41             },
 42             bindHotkeys = function (hotKeys) {
 43                 $.each(hotKeys, function (hotkey, command) {
 44                     editor.keydown(hotkey, function (e) {
 45                         if (editor.attr('contenteditable') && editor.is(':visible')) {
 46                             e.preventDefault();
 47                             e.stopPropagation();
 48                             execCommand(command);
 49                         }
 50                     }).keyup(hotkey, function (e) {
 51                         if (editor.attr('contenteditable') && editor.is(':visible')) {
 52                             e.preventDefault();
 53                             e.stopPropagation();
 54                         }
 55                     });
 56                 });
 57             },
 58             getCurrentRange = function () {
 59                 var sel = window.getSelection();
 60                 if (sel.getRangeAt && sel.rangeCount) {
 61                     return sel.getRangeAt(0);
 62                 }
 63             },
 64             saveSelection = function () {
 65                 selectedRange = getCurrentRange();
 66             },
 67             restoreSelection = function () {
 68                 var selection = window.getSelection();
 69                 if (selectedRange) {
 70                     try {
 71                         selection.removeAllRanges();
 72                     } catch (ex) {
 73                         document.body.createTextRange().select();
 74                         document.selection.empty();
 75                     }
 76 
 77                     selection.addRange(selectedRange);
 78                 }
 79             },
 80             insertFiles = function (files) {
 81                 editor.focus();
 82                 $.each(files, function (idx, fileInfo) {
 83                     if (/^image\//.test(fileInfo.type)) {
 84                         $.when(readFileIntoDataUrl(fileInfo)).done(function (dataUrl) {
 85                             execCommand('insertimage', dataUrl);
 86                         }).fail(function (e) {
 87                             options.fileUploadError("file-reader", e);
 88                         });
 89                     } else {
 90                         options.fileUploadError("unsupported-file-type", fileInfo.type);
 91                     }
 92                 });
 93             },
 94             markSelection = function (input, color) {
 95                 restoreSelection();
 96                 if (document.queryCommandSupported('hiliteColor')) {
 97                     document.execCommand('hiliteColor', 0, color || 'transparent');
 98                 }
 99                 saveSelection();
100                 input.data(options.selectionMarker, color);
101             },
102             bindToolbar = function (toolbar, options) {
103                 toolbar.find(toolbarBtnSelector).click(function () {
104                     restoreSelection();
105                     editor.focus();
106                     execCommand($(this).data(options.commandRole));
107                     saveSelection();
108                 });
109                 toolbar.find('[data-toggle=dropdown]').click(restoreSelection);
110 
111                 toolbar.find('input[type=text][data-' + options.commandRole + ']').on('webkitspeechchange change', function () {
112                     var newValue = this.value; /* ugly but prevents fake double-calls due to selection restoration */
113                     this.value = '';
114                     restoreSelection();
115                     if (newValue) {
116                         editor.focus();
117                         execCommand($(this).data(options.commandRole), newValue);
118                     }
119                     saveSelection();
120                 }).on('focus', function () {
121                     var input = $(this);
122                     if (!input.data(options.selectionMarker)) {
123                         markSelection(input, options.selectionColor);
124                         input.focus();
125                     }
126                 }).on('blur', function () {
127                     var input = $(this);
128                     if (input.data(options.selectionMarker)) {
129                         markSelection(input, false);
130                     }
131                 });
132                 toolbar.find('input[type=file][data-' + options.commandRole + ']').change(function () {
133                     restoreSelection();
134                     if (this.type === 'file' && this.files && this.files.length > 0) {
135                         insertFiles(this.files);
136                     }
137                     saveSelection();
138                     this.value = '';
139                 });
140             },
141             initFileDrops = function () {
142                 editor.on('dragenter dragover', false)
143                     .on('drop', function (e) {
144                         var dataTransfer = e.originalEvent.dataTransfer;
145                         e.stopPropagation();
146                         e.preventDefault();
147                         if (dataTransfer && dataTransfer.files && dataTransfer.files.length > 0) {
148                             insertFiles(dataTransfer.files);
149                         }
150                     });
151             };
152         options = $.extend({}, $.fn.wysiwyg.defaults, userOptions);
153         toolbarBtnSelector = 'a[data-' + options.commandRole + '],button[data-' + options.commandRole + '],input[type=button][data-' + options.commandRole + ']';
154         bindHotkeys(options.hotKeys);
155         if (options.dragAndDropImages) {
156             initFileDrops();
157         }
158         bindToolbar($(options.toolbarSelector), options);
159         editor.attr('contenteditable', true)
160             .on('mouseup keyup mouseout', function () {
161                 saveSelection();
162                 updateToolbar();
163             });
164         $(window).bind('touchend', function (e) {
165             var isInside = (editor.is(e.target) || editor.has(e.target).length > 0),
166                 currentRange = getCurrentRange(),
167                 clear = currentRange && (currentRange.startContainer === currentRange.endContainer && currentRange.startOffset === currentRange.endOffset);
168             if (!clear || isInside) {
169                 saveSelection();
170                 updateToolbar();
171             }
172         });
173         return this;
174     };
175     $.fn.wysiwyg.defaults = {
176         hotKeys: {
177             'ctrl+b meta+b': 'bold',
178             'ctrl+i meta+i': 'italic',
179             'ctrl+u meta+u': 'underline',
180             'ctrl+z meta+z': 'undo',
181             'ctrl+y meta+y meta+shift+z': 'redo',
182             'ctrl+l meta+l': 'justifyleft',
183             'ctrl+r meta+r': 'justifyright',
184             'ctrl+e meta+e': 'justifycenter',
185             'ctrl+j meta+j': 'justifyfull',
186             'shift+tab': 'outdent',
187             'tab': 'indent'
188         },
189         toolbarSelector: '[data-role=editor-toolbar]',
190         commandRole: 'edit',
191         activeToolbarClass: 'btn-info',
192         selectionMarker: 'edit-focus-marker',
193         selectionColor: 'darkgrey',
194         dragAndDropImages: true,
195         fileUploadError: function (reason, detail) { console.log("File upload error", reason, detail); }
196     };
197 }(window.jQuery));

对应的样式设计:

 1 #editor {
 2     max-height: 250px;
 3     height: 250px;
 4     background-color: white;
 5     border-collapse: separate; 
 6     border: 1px solid rgb(204, 204, 204); 
 7     padding: 4px; 
 8     box-sizing: content-box; 
 9     -webkit-box-shadow: rgba(0, 0, 0, 0.0745098) 0px 1px 1px 0px inset; 
10     box-shadow: rgba(0, 0, 0, 0.0745098) 0px 1px 1px 0px inset;
11     border-top-right-radius: 3px; border-bottom-right-radius: 3px;
12     border-bottom-left-radius: 3px; border-top-left-radius: 3px;
13     overflow: scroll;
14     outline: none;
15 }
16 #voiceBtn {
17   width: 20px;
18   color: transparent;
19   background-color: transparent;
20   transform: scale(2.0, 2.0);
21   -webkit-transform: scale(2.0, 2.0);
22   -moz-transform: scale(2.0, 2.0);
23   border: transparent;
24   cursor: pointer;
25   box-shadow: none;
26   -webkit-box-shadow: none;
27 }
28 
29 div[data-role="editor-toolbar"] {
30   -webkit-user-select: none;
31   -moz-user-select: none;
32   -ms-user-select: none;
33   user-select: none;
34 }
35 
36 .dropdown-menu a {
37   cursor: pointer;
38 }

如有错误之处,敬请指正,谢谢

posted on 2016-11-12 09:52  Cultivate  阅读(826)  评论(1编辑  收藏  举报

导航