【留言板】可編輯輸入框操作總結

小龍女先生發表於2017-04-23

閒暇之餘,用於加深自己對基礎的瞭解,徒手擼了一個留言板:輸入框。廢話少說,進入正題。簡陋的效果如下(下載程式碼):

一、定義需求

  1. 可輸入文字,以及插入表情。
  2. 相容性:IE與標準瀏覽器

二、詳細設計

根據需求,我們大致可以想到如下問題:

  1. 相容性的處理
    1. 事件繫結的相容性
    2. 可編輯輸入框的表情插入相容性
    3. 獲取資料的相容性
  2. 三個模組
    1. 留言板與ui互動的模組
    2. 表情展示模組
    3. 可編輯輸入框的操作模組 因此我規劃瞭如下的類結構:

  • 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值的。

相容性有以下幾個點:

  1. 事件的繫結:attacheEvent和addEventListener。
  2. classList在ie8-不支援的問題,暫時選擇的用setAttribute代替
  3. 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('##');
}

主要內容:

  1. range(選區):IE與標準瀏覽器的相容性,值得注意的IE操控選區時,需要讓被操控元素(也就是選區所在的元素)獲取焦點,否則會失敗。
  2. 標準瀏覽器range的APi可參考此地址:http://www.w3school.com.cn/xmldom/dom_range.asp
  3. 獲取數(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>

相關文章