闲暇之余,用于加深自己对基础的了解,徒手撸了一个留言板:输入框。废话少说,进入正题。简陋的效果如下(下载代码):
一、定义需求
- 可输入文本,以及插入表情。
- 兼容性:IE与标准浏览器
二、详细设计
根据需求,我们大致可以想到如下问题:
- 兼容性的处理
- 事件绑定的兼容性
- 可编辑输入框的表情插入兼容性
- 获取数据的兼容性
- 三个模块
- 留言板与ui交互的模块
- 表情展示模块
- 可编辑输入框的操作模块 因此我规划了如下的类结构:
- LeaveMsg:实现UI与留言板的交互
- FaceWrap:实现表情殂的管理,以及相应事件的响应,如显示/隐藏,获取表情,初始化表情列表等。
- SelectionUitls:实现可编辑输入框的操作,如:插入一个表情、获取数据等。 各模块的兼容性在以下细节中进行介绍。
三、代码实现
1. FaceWrap类(表情列表管理类)
var FaceWrap = function(head, cont, opts){ this.$head = head; this.$cont = cont; this.data = ['one', 'two', 'thr']; var self = this; var toggle = false; this.onClickHandHandle = function(evt){ if(!toggle){ self.$cont.style.display = 'block'; toggle = true; }else{ self.$cont.style.display = 'none'; toggle = false; } if(opts.onClickHandHandle){ opts.onClickHandHandle(toggle); } } this.onChooseImg = opts. onChooseImg || function(){} this.generalFaceImg(); this.bind(); } var facePt = FaceWrap.prototype; facePt.generalFaceImg = function(){ var fragment = document.createDocumentFragment(); for( var index =0; index < this.data.length; ++index){ var data = this.data[index]; var img = document.createElement('img'); img.setAttribute('src', '../img/face/' + data + '.jpg'); img.setAttribute('data-id', data); img.setAttribute('class','face-img'); fragment.appendChild(img); } this.$cont.appendChild(fragment); } facePt.bind = function(){ if(document.attachEvent){ this.$head.attachEvent('onclick',this.onClickHandHandle); this.$cont.attachEvent('onclick',this.onChooseImg); }else{ this.$head.addEventListener('click',this.onClickHandHandle); this.$cont.addEventListener('click',this.onChooseImg); } } facePt.hide = function(){ this.onClickHandHandle(); }
需要注意的点:
1. 在初始化表情列表(generalFaceImg)的时候,用到了Fragment(文档碎片)来提高性能;
2. 在class中设元素的display为none后,用js是获取不到此元素的display值的。
兼容性有以下几个点:
- 事件的绑定:attacheEvent和addEventListener。
- classList在ie8-不支持的问题,暂时选择的用setAttribute代替
- appendChild全都支持,append在chrome中支持,但ie不支持
2. SelectionUitls类(可编辑输入框管理类)
var SelectionUitls = function(dom){ this.dom = dom; this.cursorIndex; } var pt = SelectionUitls.prototype; pt.insertDomForStandard = function(dom){ var sel = window.getSelection(); //获取选区集合 var range = sel.getRangeAt(0); //获取第一个选择 range.deleteContents(); //删除选区选重的元素 range.insertNode(dom); //插入元素在选区的首位置 range = range.cloneRange(); //克隆一个选区 range.setStartAfter(dom); //设置选区起点光标位置在指定元素的后面 range.collapse(true);//合并起点、终点光标 sel.removeAllRanges();//移除所有选区 sel.addRange(range); //添加一个选区 } pt.insertDomForIe = function(dom){ this.dom.focus(); var wrap = document.createElement('div'); wrap.appendChild(dom); document.selection.createRange().pasteHTML(wrap.innerHTML); } pt.insertDom = function(dom){ //光标处插入非元素 if(window.getSelection){ this.insertDomForStandard(dom); }else{ this.insertDomForIe(dom); } } pt.getContent = function(){ //获取数据 var nodes = this.dom.childNodes; var datas = []; for(var index = 0; index < nodes.length; index ++){ var node = nodes[index]; if(node.nodeType == 3){ datas.push(node.textContent || node.nodeValue || node.data); }else{ datas.push(node.getAttribute('data-id')); } } return datas.join('##'); }
主要内容:
- range(选区):IE与标准浏览器的兼容性,值得注意的IE操控选区时,需要让被操控元素(也就是选区所在的元素)获取焦点,否则会失败。
- 标准浏览器range的APi可参考此地址:http://www.w3school.com.cn/xmldom/dom_range.asp
- 获取数(getContent):将html结构的数据转换为标准的数据,防止脚本攻击。
3. LeaveMsg类(留言板管理类)
var LeaveMsg = function(opts){ this.opts = opts; this.createFaceWrap(); this.createUitls(); this.curLocation; } var leaveMsgPt = LeaveMsg.prototype; leaveMsgPt._insertFace = function(id){ var img = document.createElement('img'); img.setAttribute('class','face-img'); img.setAttribute('data-id', id); img.src= '../img/face/' + id + '.jpg'; this.selectionUitls.insertDom(img); this.faceWrap.hide(); } leaveMsgPt.createFaceWrap = function(){ var self = this; var faceOpt = { onChooseImg:function(evt){ //插入表情,获取位置,获取表情,插入表情 var id = (evt.target||evt.srcElement).getAttribute('data-id'); self._insertFace(id); } } this.faceWrap = new FaceWrap(this.opts.faceHead, this.opts.faceCont, faceOpt); } leaveMsgPt.createUitls = function(){ this.selectionUitls = new SelectionUitls(this.opts.area); } leaveMsgPt.getContent = function(evt){ this.selectionUitls.getContent(); }
实现FaceWrap、SelectionUitls类与LeaveMsg类的组合,并对UI提供相就的API。
四、使用他们
js部分代码
var leaveMsgArea = document.getElementById('leaveMsgArea'); var faceHead = document.getElementById('head'); var faceCont = document.getElementById('cont'); var leaveMsg = new LeaveMsg({ area: leaveMsgArea, faceHead: faceHead, faceCont: faceCont });
HTML部分代码
<div class="leaveMsgArea" contenteditable="true" id="leaveMsgArea"> </div> <div class="face-wrap"> <a class="face-head" id="head" href="javascript:void(0)">表情</a> <div class="face-cont" id="cont"> </div> </div> <div class="button-group"> <button type="button" onclick="leaveMsg.getContent(event)" >获取内容</button> </div>