js 公式編輯器 - 自定義匹配規(guī)則 - 帶提示下拉框 - 動態(tài)獲取光標像素坐標
引言
前段時間發(fā)了一個編輯器的插件,忙完后自己再次進行了詳細的測試,然后心里冒出一句:“這誰寫的這么奇葩的插件?完全沒什么luan用啊!”
自己做了讓自己不滿意的事,咋整?男人不怕累,花了時間重寫(為世界上所有像我一樣勤勞的男人點贊)~
思維導圖
在小生看來,在開發(fā)每一個新功能的時候都應該做到心中有一張思維導圖:功能實現(xiàn)邏輯和實現(xiàn)功能大致的方法。當然我們不可能在還沒動手
前就考慮得面面俱到,但在正式開發(fā)之前心里對整個流程有個清晰的印象肯定會讓我們在動手時愈加流暢(喝口娃哈哈美滋滋,看圖~):

流程效果圖

觸發(fā)檢索事件字符可自定義,默認為 $,輸入 $ 觸發(fā)檢索顯示,此時檢索值為空,所以顯示所有選項,繼續(xù)輸入 a ,檢索值為 a,顯示匹配選項,當再輸入 . 時, 檢索值獲取條件發(fā)生改變(具體我們等下看代碼),
圖四中為整個流程在控制臺中的記錄。
js代碼 -- 監(jiān)聽輸入框
全局變量
考慮到里面小方法比較多,為了簡化代碼,這里我選擇模塊化一下,需要用到以下全局變量。這里特別提一下:持續(xù)事件和點事件的區(qū)別,持續(xù)顧名思義,持續(xù)事件就是一直觸發(fā)的事件,這里 $ 觸發(fā)檢索事件后,檢索值 selectVal
是變化的,但是我們又不需要它一直處于觸發(fā)狀態(tài),怎么辦呢?對,開關,我們可以給這個事件設置一個開關,條件滿足時打開開關,事件持續(xù)觸發(fā),結束后關閉開關,結束檢索事件,這里設置的開關是:searchStart;而點事件
這里就是輸入 . 時觸發(fā)的事件,它只需要在輸入 . 時獲取相關的值就行了,不需要連續(xù)觸發(fā),這里我們設置參數(shù) 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,我們需要知道事件觸發(fā)時光標所在的位置,即 oldCurrentPos,以及光標當前位置 currentPos,有了這兩個 下標,我們才能動態(tài)獲取 selectVal
獲取光標當前位置
關于獲取輸入框光標以及獲取值等方法,不了解的朋友可以去看一下 range 方法,當然無數(shù)前輩已經做過無數(shù)歸納總結講解(向前輩們敬禮~):
// 獲取當前光標位置 currentPos
var getStart =function() {
var all_range = '';
if (navigator.userAgent.indexOf("MSIE") > -1) { //IE
if( _this.get(0).tagName == "TEXTAREA" ){
// 根據(jù)body創(chuàng)建textRange
all_range = document.body.createTextRange();
// 讓textRange范圍包含元素里所有內容
all_range.moveToElementText(_this.get(0));
} else {
// 根據(jù)當前輸入元素類型創(chuàng)建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移動到之前創(chuàng)建的textRange的currentPos處, 此時當前textRange范圍變?yōu)檎麄€內容的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;
};
獲取檢索值開始位置
檢索開始位置,即事件觸發(fā)時光標所在位置,直白來說,就是把事件觸發(fā)時光標所在位置 currentPos 賦值給 oldCurrentPos 儲存起來,然后與新的 currentPos 組
成的區(qū)域 (oldCurrentPos,currentPos)就是我們檢索值所在區(qū)域
// 獲取檢索值開始位置 oldCurrentPos
var getOldCurrentPos = function(){
getStart(); // 開始輸入的時候的光標位置 currentPos
oldCurrentPos = currentPos; // 儲存輸入開始位置
console.log(oldCurrentPos);
}
設置光標位置
選擇當前項重組輸入框 value 值后光標是默認顯示在最后的,這當然不符合我們的開發(fā)需求,我們想要的效果是事件結束時光標能在我們編輯結束的位置(關于value值重組我們在下面的方法中再看)
// 設置光標位置
var setCarePosition = function(start,end) {
if(navigator.userAgent.indexOf("MSIE") > -1){
var all_range = '';
if( _this.get(0).tagName == "TEXTAREA" ){
// 根據(jù)body創(chuàng)建textRange
all_range = document.body.createTextRange();
// 讓textRange范圍包含元素里所有內容
all_range.moveToElementText(_this.get(0));
} else {
// 根據(jù)當前輸入元素類型創(chuàng)建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);
}
};
結束檢索事件
在結束檢索事件中我們需要初始化下拉框以及關閉開關,這里需要將該方法聲明在獲取檢索值方法前面,因為獲取值后整個事件流程結束,我們需要初始化變量為下一次事件觸發(fā)做好準備
// 結束檢索事件
var endSearch = function(){
_this.dropdown.find("li").remove(); // 移除下拉框中的選項
_this.dropdown.hide(); // 隱藏下拉框
searchStart = false; // 初始化檢索開關 searchStart
enterCharacter=''; // 初始化當前字符
}
獲取檢索的值
看下方代碼,我們能夠獲取值的前提是 searchStart 開關 打開狀態(tài),這里我們?yōu)榱吮3植寮撵`活性,將觸發(fā)字符設置為變量,這里默認為 $ 和 . ,enterCharacter為當前輸入的字符,
因為當我們輸入 . 時,selectVal 的獲取規(guī)則會改變,所以這里我們需要將 selectVal 獲取方式區(qū)分開來,注意:這里我們要考慮到存在一個操作 -- 回刪,輸入 $,下拉框出來了,但是我
們又覺得此處 $ 出現(xiàn)得還不是時候(反正就是要刪),刪除 $,那么檢索事件也就結束,初始化相關變量。當輸入的是 . 時,如果要替換值,那么我們需要的獲取從 . 在的位置往后找
到離 . 最近的 $ 符號,得到其在文本中的位置,這樣我們才能重組 value
// 獲取檢索的值 selctVal
var getSelectVal = function(){
var val = _this.val();
if( searchStart == true && enterCharacter != opts.levelCharacter ){ // 當輸入的是字符 triggerCharacter 的時候 默認為 $
selectVal = val.substring(oldCurrentPos,currentPos); // 檢索值直接為獲取的文本區(qū)域
}
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("獲取的值區(qū)域為:"+oldCurrentPos+"-"+currentPos);
if( oldCurrentPos > currentPos ){ // 回刪時清除選項li 隱藏下拉框
endSearch()
}
}
改變輸入框 value 值,定位光標位置
因為我們這里存在兩種選擇方式,鼠標點擊和按 enter 鍵,兩者的區(qū)別只在于執(zhí)行事件的方式,將同樣的代碼寫兩遍未免有點不美,這里我們將它摘出來
注意:此處需要區(qū)分觸發(fā)檢索事件的符號是 $ 還是 . ,因為符號不同,我們獲取的值是不同的,光標定位也是不同
// 選中l(wèi)i當前項 改變輸入框value值 定位光標
var changeValue = function(){
var val = _this.val();
var liTxt = _this.dropdown.find(".active").text();
var liTxtLength = liTxt.length;
var valLength = val.length;
// 此處需要區(qū)分觸發(fā)檢索事件的符號是
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
}
定義回調函數(shù)
獲取檢索值之后就需要發(fā)送請求了,我們拿到返回的數(shù)組 arr_json 后,將其遍歷生成 li 添加到下拉框中
// 定義回調函數(shù) 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()
});
阻止鍵盤默認事件
這里我們需要判斷下拉框的狀態(tài):顯示還是隱藏
//下拉框顯示時 阻止鍵盤方向鍵默認事件
_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事件:”我們能實時監(jiān)聽輸入框;也能通過按鍵切換當前項以及改變光標位置;也能限制輸入字符范圍,比如這里:當輸入某些字符時,將會被認為輸入了不合法字符而終止檢索事件;
我們的事件開關也是通過該事件能改變其狀態(tài)的以及 enter 鍵選取當前項
// 監(jiān)聽輸入框value值變化
_this.keyup(function(e){
var val = _this.val();
// 當前項索引
var n = _this.dropdown.find(".active").index();
// li 個數(shù)
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 != " "){ // 輸入的字符合法 可以執(zhí)行檢索事件
//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("輸入了$或者.");
// 輸入了 $,打開開關,允許檢索事件執(zhí)行
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){
// 調用回調函數(shù)
callbacktips(arr_json);
});
}
}
if( e.keyCode == 13 ){ // 按enter鍵選取當前l(fā)i文本值 重組輸入框 value值
var dropdownIsshow = _this.dropdown.css("display");
if( dropdownIsshow == "block" ){ // 為了在下拉框隱藏時按 enter鍵 能換行,需要加上這個判斷
changeValue();
console.log("這是點擊enter后searchStart:"+searchStart);
}
}
console.log("這是整個事件執(zhí)行完成以后:"+searchStart);
});
鼠標滑入切換當前項
// 切換當前項
_this.dropdown.on('mouseenter','li',function(){
$(this).addClass("active").siblings().removeClass("active");
});
點擊選取當前項 失去焦點事件
這里采用了 event.target 方法來獲得事件源,如果是 下拉框中的 li ,則執(zhí)行 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代碼 -- 動態(tài)獲取光標位置
這個方法是借鑒一位前輩的,這里附上原文地址(前輩大善):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代碼 -- 設置默認參數(shù)
var defaults = {
triggerCharacter : '$', // 默認觸發(fā)事件 字符
levelCharacter: '.', // 默認多層檢索觸發(fā)字符
dropdownWidth:'150px' // 下拉框默認寬度
};
js代碼 -- 插件調用
此處只為展示效果 在 keyPressAction 中能自定義匹配規(guī)則進行拓展
$("#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);
}
});
由于代碼比較多,這里就不展示所有代碼了,最終效果圖:

在此附上demo下載鏈接:
不管你信不信,我已經設置了下載口令,親們必須在心里說出我的一個優(yōu)點才能點擊下載~
總結
以上所述是小編給大家介紹的js 公式編輯器 - 自定義匹配規(guī)則 - 帶提示下拉框 - 動態(tài)獲取光標像素坐標,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對腳本之家網站的支持!
相關文章
Javascript實現(xiàn)html轉pdf高清版(提高分辨率)
這篇文章主要介紹了Javascript將html轉成pdf高清版(提高分辨率),需要的朋友可以參考下2020-02-02
JS實現(xiàn)獲取圖片大小和預覽的方法完整實例【兼容IE和其它瀏覽器】
這篇文章主要介紹了JS實現(xiàn)獲取圖片大小和預覽的方法,結合完整實例形式分析了javascript針對不同瀏覽器處理圖片上傳與預覽等操作的相關實現(xiàn)技巧,需要的朋友可以參考下2017-04-04
JS PHP字符串截取函數(shù)實現(xiàn)原理解析
這篇文章主要介紹了JS PHP字符串截取函數(shù)實現(xiàn)原理解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2020-08-08
js采用concat和sort將N個數(shù)組拼接起來的方法
這篇文章主要介紹了js采用concat和sort將N個數(shù)組拼接起來的方法,涉及JavaScript針對數(shù)組的合并與排序操作相關技巧,需要的朋友可以參考下2016-01-01

