js 公式编辑器 - 自定义匹配规则 - 带提示下拉框 - 动态获取光标像素坐标


Posted in Javascript onJanuary 04, 2018

引言

前段时间发了一个编辑器的插件,忙完后自己再次进行了详细的测试,然后心里冒出一句:“这谁写的这么奇葩的插件?完全没什么luan用啊!”

自己做了让自己不满意的事,咋整?男人不怕累,花了时间重写(为世界上所有像我一样勤劳的男人点赞)~

思维导图

在小生看来,在开发每一个新功能的时候都应该做到心中有一张思维导图:功能实现逻辑和实现功能大致的方法。当然我们不可能在还没动手

前就考虑得面面俱到,但在正式开发之前心里对整个流程有个清晰的印象肯定会让我们在动手时愈加流畅(喝口娃哈哈美滋滋,看图~):

js 公式编辑器 - 自定义匹配规则 - 带提示下拉框 - 动态获取光标像素坐标

流程效果图

js 公式编辑器 - 自定义匹配规则 - 带提示下拉框 - 动态获取光标像素坐标

触发检索事件字符可自定义,默认为 $,输入 $ 触发检索显示,此时检索值为空,所以显示所有选项,继续输入 a ,检索值为 a,显示匹配选项,当再输入 . 时, 检索值获取条件发生改变(具体我们等下看代码),

图四中为整个流程在控制台中的记录。

js代码 -- 监听输入框

 全局变量

 考虑到里面小方法比较多,为了简化代码,这里我选择模块化一下,需要用到以下全局变量。这里特别提一下:持续事件和点事件的区别,持续顾名思义,持续事件就是一直触发的事件,这里 $ 触发检索事件后,检索值 selectVal

 是变化的,但是我们又不需要它一直处于触发状态,怎么办呢?对,开关,我们可以给这个事件设置一个开关,条件满足时打开开关,事件持续触发,结束后关闭开关,结束检索事件,这里设置的开关是:searchStart;而点事件

 这里就是输入 . 时触发的事件,它只需要在输入 . 时获取相关的值就行了,不需要连续触发,这里我们设置参数 enterCharacter : 当前输入的字符

var _this = $(this);
var e = event || window.event; // 键值兼容
var searchStart = false; // 设置检索事件开关
var checkCharacter = false; // 输入字符检索开关  
var oldCurrentPos = ''; // 检索值开始的位置
var currentPos = ''; // 检索值结束的位置
var selectVal = ''; // 检索值
var pos = ''; // 设置光标位置
var enterCharacter = ''; // 当前输入的字符
var dotVal; // 输入 . 时从0到当前光标位置文本
var dotDollerPos; // 获取往后查找离 . 最近的 $ 的下标,引文输入 . 时的检索值即dotSelectVal不包含 $ 本身,所以需要加1 
var dotSelectVal; // 输入 . 时的检索值

  插入输入框 

  首先插入下拉框,当然留到后面插入也可以(你开心你说什么都是对的),但是这里有个点需要注意一下:为什么选择插入在body下?因为我们获取到的下拉框的位置是绝对定位坐标。

// 插入下拉框
 _this.dropdown = $('<ul class="editTips" style="display:none;"></ul>');
// 获取到的弹出的下拉框的位置是绝对定位的坐标,所以得把弹出的层放到$("body").after(_this.dropdown);   
 _this.dropdown.css({
 'width':opts.dropdownWidth,
 'position':'absolute',
 });
 _this.css({
 'position': 'relative',
 });

 注意:这里我们提一下,要获取检索值,即 selectVal,我们需要知道事件触发时光标所在的位置,即 oldCurrentPos,以及光标当前位置 currentPos,有了这两个 下标,我们才能动态获取 selectVal

 获取光标当前位置

  关于获取输入框光标以及获取值等方法,不了解的朋友可以去看一下 range 方法,当然无数前辈已经做过无数归纳总结讲解(向前辈们敬礼~):

// 获取当前光标位置 currentPos
  var getStart =function() {  
   var all_range = '';
   if (navigator.userAgent.indexOf("MSIE") > -1) { //IE
   if( _this.get(0).tagName == "TEXTAREA" ){ 
    // 根据body创建textRange
    all_range = document.body.createTextRange();
    // 让textRange范围包含元素里所有内容
    all_range.moveToElementText(_this.get(0));
   } else {
    // 根据当前输入元素类型创建textRange
    all_range = _this.get(0).createTextRange();
   }
   // 输入元素获取焦点
   _this.focus();
   // 获取当前的textRange,如果当前的textRange是一个具体位置而不是范围,textRange的范围从currentPos到end.此时currentPos等于end
   var cur_range = document.selection.createRange();
   // 将当前的textRange的end向前移"选中的文本.length"个单位.保证currentPos=end
   cur_range.moveEnd('character',-cur_range.text.length)
   // 将当前textRange的currentPos移动到之前创建的textRange的currentPos处, 此时当前textRange范围变为整个内容的currentPos处到当前范围end处
   cur_range.setEndPoint("StartToStart",all_range);

   // 此时当前textRange的Start到End的长度,就是光标的位置
   currentPos = cur_range.text.length;
   } else {
   // 文本框获取焦点
   _this.focus();
   // 获取当前元素光标位置
   currentPos = _this.get(0).selectionStart;
   //console.log("光标当前位置:"+currentPos);
   }
   // 返回光标位置
   return currentPos;
  };

               

获取检索值开始位置

检索开始位置,即事件触发时光标所在位置,直白来说,就是把事件触发时光标所在位置 currentPos 赋值给 oldCurrentPos 储存起来,然后与新的 currentPos 组

成的区域 (oldCurrentPos,currentPos)就是我们检索值所在区域 

               

// 获取检索值开始位置 oldCurrentPos
  var getOldCurrentPos = function(){
   getStart(); // 开始输入的时候的光标位置 currentPos
   oldCurrentPos = currentPos; // 储存输入开始位置
   console.log(oldCurrentPos);
  }

设置光标位置

选择当前项重组输入框 value 值后光标是默认显示在最后的,这当然不符合我们的开发需求,我们想要的效果是事件结束时光标能在我们编辑结束的位置(关于value值重组我们在下面的方法中再看)               

// 设置光标位置
  var setCarePosition = function(start,end) {
   if(navigator.userAgent.indexOf("MSIE") > -1){
   var all_range = '';
   if( _this.get(0).tagName == "TEXTAREA" ){ 
    // 根据body创建textRange
    all_range = document.body.createTextRange();
    // 让textRange范围包含元素里所有内容
    all_range.moveToElementText(_this.get(0));
   } else {
    // 根据当前输入元素类型创建textRange
    all_range = _this.get(0).createTextRange();
   }
   _this.focus();
   // 将textRange的start设置为想要的start
   all_range.moveStart('character',start);
   // 将textRange的end设置为想要的end. 此时我们需要的textRange长度=end-start; 所以用总长度-(end-start)就是新end所在位置
   all_range.moveEnd('character',-(all_range.text.length-(end-start)));
   // 选中从start到end间的文本,若start=end,则光标定位到start处
   all_range.select();
   }else{
   // 文本框获取焦点
   _this.focus();
   // 选中从start到end间的文本,若start=end,则光标定位到start处
   _this.get(0).setSelectionRange(start,end);
   }
  };

结束检索事件

在结束检索事件中我们需要初始化下拉框以及关闭开关,这里需要将该方法声明在获取检索值方法前面,因为获取值后整个事件流程结束,我们需要初始化变量为下一次事件触发做好准备             

// 结束检索事件
  var endSearch = function(){
   _this.dropdown.find("li").remove(); // 移除下拉框中的选项
   _this.dropdown.hide(); // 隐藏下拉框
   searchStart = false; // 初始化检索开关 searchStart
   enterCharacter=''; // 初始化当前字符
  }

 

获取检索的值

看下方代码,我们能够获取值的前提是 searchStart 开关 打开状态,这里我们为了保持插件的灵活性,将触发字符设置为变量,这里默认为 $ 和 . ,enterCharacter为当前输入的字符,

        因为当我们输入 . 时,selectVal 的获取规则会改变,所以这里我们需要将 selectVal 获取方式区分开来,注意:这里我们要考虑到存在一个操作 -- 回删,输入 $,下拉框出来了,但是我

        们又觉得此处 $ 出现得还不是时候(反正就是要删),删除 $,那么检索事件也就结束,初始化相关变量。当输入的是 . 时,如果要替换值,那么我们需要的获取从 . 在的位置往后找

到离 . 最近的 $ 符号,得到其在文本中的位置,这样我们才能重组 value            

// 获取检索的值 selctVal
  var getSelectVal = function(){
   var val = _this.val();
   if( searchStart == true && enterCharacter != opts.levelCharacter ){ // 当输入的是字符 triggerCharacter 的时候 默认为 $
   selectVal = val.substring(oldCurrentPos,currentPos); // 检索值直接为获取的文本区域
   }
   if( searchStart == true && enterCharacter == opts.levelCharacter ){ // 当输入的是字符 levelCharacter 的时候 默认为 .
   dotVal = val.slice(0,currentPos);
   dotDollerPos = dotVal.lastIndexOf(opts.triggerCharacter)+1;
   dotSelectVal = dotVal.substring(dotDollerPos,currentPos);
   selectVal = dotSelectVal;
   console.log("到当前下标的字符串为:"+dotVal);
   console.log("到当前下标最近的$下标是:"+dotDollerPos);
   console.log("输入 . 时检索值为:"+dotSelectVal);
   }  
   console.log("获取的值区域为:"+oldCurrentPos+"-"+currentPos);
   if( oldCurrentPos > currentPos ){ // 回删时清除选项li 隐藏下拉框
   endSearch()
   }  
  }

改变输入框 value 值,定位光标位置

因为我们这里存在两种选择方式,鼠标点击和按 enter 键,两者的区别只在于执行事件的方式,将同样的代码写两遍未免有点不美,这里我们将它摘出来

注意:此处需要区分触发检索事件的符号是 $ 还是 . ,因为符号不同,我们获取的值是不同的,光标定位也是不同            

// 选中li当前项 改变输入框value值 定位光标
  var changeValue = function(){
   var val = _this.val(); 
   var liTxt = _this.dropdown.find(".active").text();
   var liTxtLength = liTxt.length;
   var valLength = val.length;
   // 此处需要区分触发检索事件的符号是
   if( enterCharacter == opts.levelCharacter ){ // 如果是 .
   var beforeSelectVal = val.substring(0,dotDollerPos);  
   }
   else{ // 如果是 &
   var beforeSelectVal = val.substring(0,oldCurrentPos);
   }
   var beforeSelectValLength = beforeSelectVal.length;
   var afterSelectVal = val.substring(currentPos,valLength);
   var pos = liTxtLength + beforeSelectValLength;
   val = beforeSelectVal+liTxt+afterSelectVal;
   _this.val(val);
   setCarePosition(pos,pos); // 将光标定位在插入值后面
   endSearch();
   console.log("文本长度:"+beforeSelectVal.length);
   console.log("li文本为:"+liTxt);
   console.log("前部为:"+beforeSelectVal);
   console.log("后部分为:"+afterSelectVal);
   return false; // 此处必须加上return false 不然会调用callbacktips 初始化 dropdown
  }

定义回调函数

    获取检索值之后就需要发送请求了,我们拿到返回的数组 arr_json 后,将其遍历生成 li 添加到下拉框中              

// 定义回调函数 callbacktips
  var callbacktips = function(arr_json){
   // 初始化 UL 
   _this.dropdown.find("li").remove();
   if( arr_json ){
   for( i=0;i<arr_json.length;i++ ){
    var n = arr_json[i].indexOf(selectVal);
    if( n != -1 ){
    _this.dropdown.append('<li>'+arr_json[i]+'</li>'); 
    }else{
    return;
    }     
   };
   }   
   _this.dropdown.show();
   _this.dropdown.find("li:first-child").addClass("active");
   // 自定义样式
   _this.dropdown.find("li").css({ 
   'width':'100%',
   });
  };

获得焦点时获取光标位置

这里我们直接调用上面的方法就行了

 

// 获得焦点的时候获取光标位置
  _this.click(function(){
   getOldCurrentPos()
  });

阻止键盘默认事件

这里我们需要判断下拉框的状态:显示还是隐藏

 //下拉框显示时 阻止键盘方向键默认事件
  _this.keydown(function(e){
   var dropdownIsshow = _this.dropdown.css("display");
   if( dropdownIsshow == "block" ){
   if( e.keyCode == 38 || e.keyCode == 40 || e.keyCode == 13 ){
    e.preventDefault();
   }
   }
  })

keyup 事件

通过keyuo事件:”我们能实时监听输入框;也能通过按键切换当前项以及改变光标位置;也能限制输入字符范围,比如这里:当输入某些字符时,将会被认为输入了不合法字符而终止检索事件;

我们的事件开关也是通过该事件能改变其状态的以及 enter 键选取当前项

// 监听输入框value值变化
  _this.keyup(function(e){
   var val = _this.val(); 
   // 当前项索引
   var n = _this.dropdown.find(".active").index();
   // li 个数
   var n_max = _this.dropdown.find("li").length;  
   getStart(); // 获得最新光标位置
   // 方向键控制 li 选项
   if( e.keyCode == 38 ){   
   if(n-1>=0){
    _this.dropdown.find('li').eq(n-1).addClass("active").siblings().removeClass("active");
   }
   if( n == 0){
    _this.dropdown.find('li').eq(n_max-1).addClass("active").siblings().removeClass("active");
   }
   return false; // 此处必须加上return false 不然会重复初始化
   }  
   if( e.keyCode == 40 ){
   if(n<n_max-1){
    _this.dropdown.find('li').eq(n+1).addClass("active").siblings().removeClass("active"); 
   }
   if( n+1 == n_max ){
    _this.dropdown.find('li').eq(0).addClass("active").siblings().removeClass("active");
   }
   return false; // 此处必须加上return false 不然会重复初始化
   } 
   if( e.keyCode != 37 && e.keyCode != 38 && e.keyCode != 39 && e.keyCode != 40 ){
   var reg = new RegExp("[`~!@#^&*()=|{}':;',\\[\\]<>/?~!@#¥……&*()——|{}【】‘;:”“'。,、?]");
   enterCharacter = val.substring(currentPos-1,currentPos); // 当前输入的字符
   //console.log(enterCharacter);
   if( reg.test(enterCharacter) == false && enterCharacter != " "){ // 输入的字符合法 可以执行检索事件
    //console.log("输入字符合法");
    checkCharacter = true;
   }else{
    checkCharacter = false;
    endSearch()
    console.log("输入了不合法字符");
    //console.log(selectVal);   
   }   
   }   
   console.log("当前输入的字符是:"+enterCharacter);
   if( enterCharacter == opts.triggerCharacter || enterCharacter == opts.levelCharacter){
   console.log("输入了$或者.");
   // 输入了 $,打开开关,允许检索事件执行
   searchStart = true;
   getOldCurrentPos(); // 输入 $ 的时候重置 oldCurrentPos
   }
   getSelectVal(); // 外度调用获取检索值方法 保证实时更新 selectVal 及 searchStart
   if( searchStart == true && checkCharacter == true && e.keyCode != 13 ){
   console.log("获取的值:"+selectVal);
   if( $.isFunction(opts.keyPressAction) ){   
    opts.keyPressAction(selectVal, function(arr_json){
    // 调用回调函数
    callbacktips(arr_json);   
    });
   }
   }
   if( e.keyCode == 13 ){ // 按enter键选取当前li文本值 重组输入框 value值
   var dropdownIsshow = _this.dropdown.css("display");
   if( dropdownIsshow == "block" ){ // 为了在下拉框隐藏时按 enter键 能换行,需要加上这个判断
    changeValue();
    console.log("这是点击enter后searchStart:"+searchStart);
   }
   }
   console.log("这是整个事件执行完成以后:"+searchStart);
  });

鼠标滑入切换当前项

 

 // 切换当前项
  _this.dropdown.on('mouseenter','li',function(){
   $(this).addClass("active").siblings().removeClass("active");
  });

点击选取当前项 失去焦点事件

这里采用了 event.target 方法来获得事件源,如果是 下拉框中的 li ,则执行 changeValue() 方法,否则结束检索事件 endSearch()

 

// 点击当前项获取文本值 重组输入框 value值 失去焦点时隐藏下拉框 清空下拉框
  $(document).click(function(e){
   var e = event || window.event;
   var el = e.target.localName; // 获取事件源 标签名
   el == "li" ? changeValue() : endSearch();
   //console.log(el);
  })

js代码 -- 动态获取光标位置

这个方法是借鉴一位前辈的,这里附上原文地址(前辈大善):http://blog.csdn.net/kingwolfofsky/article/details/6586029

/*********以下为获取下拉框像素坐标方法*********/ 
  var kingwolfofsky = { 
   getInputPositon: function (elem) { 
   if (document.selection) { //IE Support 
    elem.focus(); 
    var Sel = document.selection.createRange(); 
    return { 
    left: Sel.boundingLeft, 
    top: Sel.boundingTop, 
    bottom: Sel.boundingTop + Sel.boundingHeight 
    }; 
   } else { 
    var that = this; 
    var cloneDiv = '{$clone_div}', cloneLeft = '{$cloneLeft}', cloneFocus = '{$cloneFocus}', cloneRight = '{$cloneRight}'; 
    var none = '<span style="white-space:pre-wrap;"> </span>'; 
    var div = elem[cloneDiv] || document.createElement('div'), focus = elem[cloneFocus] || document.createElement('span'); 
    var text = elem[cloneLeft] || document.createElement('span'); 
    var offset = that._offset(elem), index = this._getFocus(elem), focusOffset = { left: 0, top: 0 }; 
   
    if (!elem[cloneDiv]) { 
    elem[cloneDiv] = div, elem[cloneFocus] = focus; 
    elem[cloneLeft] = text; 
    div.appendChild(text); 
    div.appendChild(focus); 
    document.body.appendChild(div); 
    focus.innerHTML = '|'; 
    focus.style.cssText = 'display:inline-block;width:0px;overflow:hidden;z-index:-100;word-wrap:break-word;word-break:break-all;'; 
    div.className = this._cloneStyle(elem); 
    div.style.cssText = 'visibility:hidden;display:inline-block;position:absolute;z-index:-100;word-wrap:break-word;word-break:break-all;overflow:hidden;'; 
    }; 
    div.style.left = this._offset(elem).left + "px"; 
    div.style.top = this._offset(elem).top + "px"; 
    var strTmp = elem.value.substring(0, index).replace(/</g, '<').replace(/>/g, '>').replace(/\n/g, '<br/>').replace(/\s/g, none); 
    text.innerHTML = strTmp; 
   
    focus.style.display = 'inline-block'; 
    try { focusOffset = this._offset(focus); } catch (e) { }; 
    focus.style.display = 'none'; 
    return { 
    left: focusOffset.left, 
    top: focusOffset.top, 
    bottom: focusOffset.bottom 
    }; 
   } 
   },
   // 克隆元素样式并返回类 
   _cloneStyle: function (elem, cache) { 
   if (!cache && elem['${cloneName}']) return elem['${cloneName}']; 
   var className, name, rstyle = /^(number|string)$/; 
   var rname = /^(content|outline|outlineWidth)$/; //Opera: content; IE8:outline && outlineWidth 
   var cssText = [], sStyle = elem.style; 
   
   for (name in sStyle) { 
    if (!rname.test(name)) { 
    val = this._getStyle(elem, name); 
    if (val !== '' && rstyle.test(typeof val)) { // Firefox 4 
     name = name.replace(/([A-Z])/g, "-$1").toLowerCase(); 
     cssText.push(name); 
     cssText.push(':'); 
     cssText.push(val); 
     cssText.push(';'); 
    }; 
    }; 
   }; 
   cssText = cssText.join(''); 
   elem['${cloneName}'] = className = 'clone' + (new Date).getTime(); 
   this._addHeadStyle('.' + className + '{' + cssText + '}'); 
   return className; 
   }, 
   
   // 向页头插入样式 
   _addHeadStyle: function (content) { 
   var style = this._style[document]; 
   if (!style) { 
    style = this._style[document] = document.createElement('style'); 
    document.getElementsByTagName('head')[0].appendChild(style); 
   }; 
   style.styleSheet && (style.styleSheet.cssText += content) || style.appendChild(document.createTextNode(content)); 
   }, 
   _style: {}, 
   
   // 获取最终样式 
   _getStyle: 'getComputedStyle' in window ? function (elem, name) { 
   return getComputedStyle(elem, null)[name]; 
   } : function (elem, name) { 
   return elem.currentStyle[name]; 
   }, 
   // 获取光标在文本框的位置 
   _getFocus: function (elem) { 
   var index = 0; 
   if (document.selection) {// IE Support 
    elem.focus(); 
    var Sel = document.selection.createRange(); 
    if (elem.nodeName === 'TEXTAREA') {//textarea 
    var Sel2 = Sel.duplicate(); 
    Sel2.moveToElementText(elem); 
    var index = -1; 
    while (Sel2.inRange(Sel)) { 
     Sel2.moveStart('character'); 
     index++; 
    }; 
    } 
    else if (elem.nodeName === 'INPUT') {// input 
    Sel.moveStart('character', -elem.value.length); 
    index = Sel.text.length; 
    } 
   } 
   else if (elem.selectionStart || elem.selectionStart == '0') { // Firefox support 
    index = elem.selectionStart; 
   } 
   return (index); 
   }, 
   
   // 获取元素在页面中位置 
   _offset: function (elem) { 
   var box = elem.getBoundingClientRect(), doc = elem.ownerDocument, body = doc.body, docElem = doc.documentElement; 
   var clientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0; 
   var top = box.top + (self.pageYOffset || docElem.scrollTop) - clientTop, left = box.left + (self.pageXOffset || docElem.scrollLeft) - clientLeft; 
   return { 
    left: left, 
    top: top, 
    right: left + box.width, 
    bottom: top + box.height 
   }; 
   } 
  };

调用获取坐标方法

  // 调用获取坐标方法 show(elem)
  $(this).keyup(function(){
   show(this);
  });  
  // 调用 kingwolfofsky, 获取光标坐标
  function show(elem) { 
   var p = kingwolfofsky.getInputPositon(elem); 
   var s = _this.dropdown.get(0); 
   var ttop = parseInt(_this.css("marginTop"));
   var tleft = parseInt(_this.css("marginLeft"));
   s.style.top = p.bottom-ttop+10+'px'; 
   s.style.left = p.left-tleft + 'px';   
  }

js代码 -- 设置默认参数

var defaults = { 
 triggerCharacter : '$', // 默认触发事件 字符
 levelCharacter: '.', // 默认多层检索触发字符
 dropdownWidth:'150px' // 下拉框默认宽度
 };

js代码 -- 插件调用

此处只为展示效果 在 keyPressAction 中能自定义匹配规则进行拓展

 

 $("#test").editTips({
  triggerCharacter : '$',
  levelCharacter: '.',
  dropdownWidth:'150px', 
  keyPressAction:function(selectVal,callbacktips){
  var arr_json;
  if( selectVal == "" ){
   arr_json = ["a","ab","b","bb"]
  }
  if(selectVal && selectVal.indexOf("a")== 0){
   arr_json = ["a","ab"];
  }
  if(selectVal && selectVal.indexOf("b")== 0){
   arr_json = ["b","bb"];
  }
  if(selectVal && selectVal.indexOf("a.")== 0){
   arr_json = ["a.a","a.b","a.c"];
  }
  if(selectVal && selectVal.indexOf("b.")== 0){
   arr_json = ["b.a","b.b","b.c"];
  }
  if(selectVal && selectVal.indexOf("ab.")== 0){
   arr_json = ["ab.a","ab.b","ab.c"];
  }
  if(selectVal && selectVal.indexOf("bb.")== 0){
   arr_json = ["bb.a","bb.b","bb.c"];
  }
  callbacktips(arr_json);
  }  
 });

由于代码比较多,这里就不展示所有代码了,最终效果图:

js 公式编辑器 - 自定义匹配规则 - 带提示下拉框 - 动态获取光标像素坐标

在此附上demo下载链接:

不管你信不信,我已经设置了下载口令,亲们必须在心里说出我的一个优点才能点击下载~

总结

以上所述是小编给大家介绍的js 公式编辑器 - 自定义匹配规则 - 带提示下拉框 - 动态获取光标像素坐标,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!

Javascript 相关文章推荐
jquery 实现二级/三级/多级联动菜单的思路及代码
Apr 08 Javascript
js实现键盘控制DIV移动的方法
Jan 10 Javascript
jQuery表单域选择器用法分析
Feb 10 Javascript
JavaScript操作Cookie详解
Feb 28 Javascript
jQuery实现网站添加高亮突出显示效果的方法
Jun 26 Javascript
AngularJS入门教程之表格实例详解
Jul 27 Javascript
AngularJs Forms详解及简单示例
Sep 01 Javascript
Vue.js Ajax动态参数与列表显示实现方法
Oct 20 Javascript
关于javascript事件响应的基础语法总结(必看篇)
Dec 26 Javascript
angular动态删除ng-repaeat添加的dom节点的方法
Jul 20 Javascript
vue动画之点击按钮往上渐渐显示出来的实例
Sep 29 Javascript
vue.config.js中配置Vue的路径别名的方法
Feb 11 Javascript
浅谈vuex 闲置状态重置方案
Jan 04 #Javascript
详解Angular5 服务端渲染实战
Jan 04 #Javascript
JavaScript中的高级函数
Jan 04 #Javascript
Three.js 再探 - 写一个微信跳一跳极简版游戏
Jan 04 #Javascript
JS实现带导航城市列表以及输入搜索功能
Jan 04 #Javascript
微信小程序实现的贪吃蛇游戏【附源码下载】
Jan 03 #Javascript
详解Angular2学习笔记之Html属性绑定
Jan 03 #Javascript
You might like
使用PHP实现二分查找算法代码分享
2011/06/24 PHP
php中检查文件或目录是否存在的代码小结
2012/10/22 PHP
php实例分享之html转为rtf格式
2014/06/02 PHP
PHP 根据key 给二维数组分组
2016/12/09 PHP
php判断str字符串是否是xml格式数据的方法示例
2017/07/26 PHP
PHP实时统计中文字数和区别
2019/02/28 PHP
php5.3/5.4/5.5/5.6/7常见新增特性汇总整理
2020/02/27 PHP
JavaScript入门教程(11) js事件处理
2009/01/31 Javascript
基于Jquery制作的幻灯片图集效果打包下载
2011/02/12 Javascript
javascript中的document.open()方法使用介绍
2013/10/09 Javascript
jQuery Ajax()方法使用指南
2014/11/19 Javascript
javascript中$(function() {});写与不写有哪些区别
2015/08/10 Javascript
vue,angular,avalon这三种MVVM框架优缺点
2016/04/27 Javascript
jQuery插件ajaxfileupload.js实现上传文件
2020/10/23 Javascript
BootStrap入门学习第一篇
2017/08/28 Javascript
Vue 父子组件的数据传递、修改和更新方法
2018/03/01 Javascript
利用Vconsole和Fillder进行移动端抓包调试方法
2019/03/05 Javascript
什么时候不能在 Node.js 中使用 Lock Files
2019/06/24 Javascript
在layui中select更改后生效的方法
2019/09/05 Javascript
详解小程序如何改变onLoad的执行时机
2019/11/01 Javascript
vue使用自定义事件的表单输入组件用法详解【日期组件与货币组件】
2020/06/01 Javascript
在Vue中使用antv的示例代码
2020/06/29 Javascript
在vue中created、mounted等方法使用小结
2020/07/21 Javascript
[03:09]2014DOTA2国际邀请赛 赛场上的美丽风景线 中国Coser也爱DOTA2
2014/07/20 DOTA
[03:12]完美世界DOTA2联赛PWL DAY9集锦
2020/11/10 DOTA
python sys模块sys.path使用方法示例
2013/12/04 Python
使用python实现个性化词云的方法
2017/06/16 Python
python实现计数排序与桶排序实例代码
2019/03/28 Python
Python上下文管理器全实例详解
2019/11/12 Python
python tkiner实现 一个小小的图片翻页功能的示例代码
2020/06/24 Python
在Ubuntu中安装并配置Pycharm教程的实现方法
2021/01/06 Python
英国领先的在线药房:Pharmacy First
2017/09/10 全球购物
办公设备采购方案
2014/03/16 职场文书
早会主持词
2014/03/17 职场文书
2014年党员整改措施
2014/10/24 职场文书
2015年员工工作表现评语
2015/03/25 职场文书