代码优化及修正说明,新增两个功能,第一、指定parentId[用于筛选对应数据的根节点数],第二、返回所选择的数据数据及创建的class名称[用于重新绑定点击事件]。代码实例及创建代码如下:
*地址信息:
//#region 创建省份市区选择
$.getJSON("../json/region.json", function (result) {
var tempArray = eval(result);
that.cachAddress = tempArray;
});
$.ajax({
type: "GET",
url: $.stringFormat("{0}/Public/getALLPlace", $.cookie("path")),
async: true,
success: function (result) {
var jsonData = eval("(" + result + ")").data;
that.address = jsonData;
that.linkageObj = new Linkage({
arr: that.address,
containerClass: ".choiceAddress",
isConsiderParent: false,
isAddWidth: true,
addWith: 7,
returnSelectValue: function (data) {
var result = "";
var lastAreaText = "";//最后的地址文本
data.map(function (item) {
result += $.stringFormat("{0} ", item.Name);
lastAreaText = item.Name;
})
that.shippingAddressItem.ShowArea = result;
that.selectShowArea = data;
that.cachAddress.map(function (item) {
if (item.Name == lastAreaText) {
that.shippingAddressItem.ZipCode = item.ZipCode;
return false;
}
})
},
returnClass: function (clsName) {
that.linkageClass = clsName;
}
})
}
});
//#rendregion
that.linkageObj.showSelectInfoPane(that.linkageClass);此代码是重新创建绑定数据,注意我是结合vue开发使用的,具体大家看js源码自行扩展
新的下载地址
以上部分是新增优化内容,以下部分是旧说明内容
今天我们要讲的是如何利用jQuery仿京东收获地址,我们涉及的主要知识点为数组遍历,函数封装。按照惯例,我们在讲功能实现之前,先看看我们的效果展示图,图一为信息接收栏,图二为点击信息接收栏下拉选择,该结构类似于combobox。
图1 信息接收栏
图2 信息选择栏
结构分为两部分,文本选择框input,因为我们这边为了方便给input赋值之后能够模拟正常的点击效果,我们这边的input需要定义data-*类型数据,实例代码如下:
上方的代码data-*对应的值代码当前input通过数据赋值,因此点击的时候将会获取对应的id、assign、isclick来判断是否需要模拟点击,默认情况下data-id="-1" data-assign="0" data-isclick="0"。同时我们定义的json返回类型数据为:
{
ID: "BB47D6C7D2EB2EAC",
ParentId: "-1",
NavName: "03万达店"
},
为了方便大家了解整个设计过程,我先解释我们如何实现反向模拟点击。整体的设计思路如下:给对应的input添加备注,是否是赋值的如果是赋值的,获取当前的ParentId,然后根据ParentId来获取具有上下级结构的数据源,同时将当前原始的对象添加的缓存数据源中。由于我们这边的li元素的class定义为我们对应的ID,因此我们遍历li判断对应的class是否和我们缓存数组(下面用Cache来代表我们的缓存数组)第一个元素ID相等,如果相等那么模拟点击,同时对于Cache移除已经模拟点击的数据。对应的代码如下:
1、获取input对应data("id")值的对象,注:return true相当于continue;return false相当于break;
/**
* 获取当前指定ID的ParentId
* @param {Object} arr 数据源
* @param {Object} id 当前数据ID
*/
SelectParentId = function (arr, id) {
var obj;
$(arr).each(function (index, item) {
if (item.ID == id) {
obj = item;
return false;//退出整个循环
}
})
return obj;
}
2、获取具有层级结构数据,这边采用递归调用的方法来获取对应的数据,代码中的ParentJson为上述所说的Cache。
/**
* 获取当前元素上级数据集合,用于反向模拟效果
* @param {Object} oldArr 数据集
* @param {Object} pid 父节点Id
*/
SelectParentData = function (oldArr, pid) {
oldArr.map(function (item) {
if (item.ID == pid) {
var obj = {
ID: item.ID,
ParentId: item.ParentId,
NavName: item.NavName
}
SelectParentData(oldArr, item.ParentId);
ParentJosn.push(obj);
}
})
}
预备功能函数都创建完了,我们现在可以执行下一步操作,模拟点击事件处理。我们给input绑定个点击事件,对当前点击对象的备注属性isclick置为1,方便接下来的判断操作,具体代码写法为$(this).data("isclick", "1");第二步则为判断当前点击的input对象的assign值是否为1,如果是1的话代表当前是赋值的,需要模拟点击。除此之外,判断id是否为"-1"如果是"-1"代表该值为默认值,同样不需要模拟点击操作。
先获取当前input在数据源的元素对象,var obj = SelectParentId(jsonData, Id)这边的jsonData为数据源;然后获取上级结构数据SelectParentData(jsonData, obj.ParentId);同时将obj追加到Cache中 ParentJosn.push(obj);最后遍历查询li元素[这边需要判断Cache是否有值],实现代码如下:
if (ParentJosn.length > 0) {//判断当前获取的具有上级机构的数组长度大于0
$(".contentsMain .contents-center ul").find("li").each(function () {
if (ParentJosn.length > 0) {
if ($(this).hasClass(ParentJosn[0].ID)) {
RemoveArray(ParentJosn, ParentJosn[0].ID);
$(this).click();
return true;
}
}
})
}
上方的代码中的RemoveArray为删除数组数据,完整代码如下,我们采用splice对数据进行删除操作,splice() 方法可删除从 index 处开始的零个或多个元素,并且用参数列表中声明的一个或多个值来替换那些被删除的元素。如果从 arrayObject 中删除了元素,则返回的是含有被删除的元素的数组。
/**
* 删除数据
* @param arr 数据源
* @param id 元素对应的Id
* @constructor
*/
RemoveArray = function (arr, id) {
var tempIndex = 0;
$(arr).each(function (index, item) {
if (item.ID == id) {
tempIndex = index;
return true;
}
})
arr.splice(tempIndex, 1);
}
我们当前模拟点击的只是其中一个div li元素而已,因为我们点击的是li元素,我们的li元素点击之后是会查找是否含有下一级数据,如果含有下一级元素继续创建上述结构的HTML。因此我们需要在创建ul-li元素之后判断是否需要模拟点击,上述所说的input备注主要用处也是在这边体现。我们需要判断input是否是赋值的同时是否是点击触发的,然后执行上述操作,这边不再赘述,直接贴代码。
//#region 判断方向结构数据是否有值,如果有值执行下一步操作
if ($("#msg").data("assign") == "1" && $("#msg").data("isclick") == "1") {
ulHtml.find("li").each(function () {
if (ParentJosn.length > 0) {
if ($(this).hasClass(ParentJosn[0].ID)) {
RemoveArray(ParentJosn, ParentJosn[0].ID);
$(this).click();
return true;
}
} else
$("#msg").data("assign", 0);
})
}
//#endregion
至此我们的反向模拟操作就此结束,现在让我们回过头来看看我们正常操作代码如何实现。步骤是先创建代码,然后创建样式最后绑定对应的点击事件。考虑到我们页面上可能会创建多个类似的功能页面,我们这边以时间戳来创建class名称。对应创建html的写法如下:
//#region 定义变量
var self = this,
_config = self.config,
_cache = self.cache;
var _Linkage = new Date().getTime();
_Linkage = 'Y' + _Linkage;
//#endregion
//#region 创建div
var divHtml = $('' +
' ' +
' ' +
' ' +
' ' +
' ' +
' ' +
' 点击完成即为选择完成' +
' 完成' +
' 取消' +
' ' +
'');
$("body").append(divHtml);
//#endregion
当前创建的是大体容器,我们还需要创建firstPage的ul-li元素,同时考虑到我们创建的html是具有共用属性,因此我们需要创建一个方法。firstPage的页面ui-li元素数据是根节点数据,每点击li元素会以当前元素的id去获取下一级数据。获取指定ParentId数据实现方法代码如下:[注:贴出的代码是封装在js的写法]
/**
* 获取指定parentId的同级数据
* @param pid 父节点ID
* @return {Array} 返回parentId相同的数据集合
*/
selectEquativeData:function (pid) {
var self = this,
_config = self.config,
_cache = self.cache;
var tempData = [];//缓存数据
$(_config.arr).each(function (index, item) {
if (item.ParentId == pid) {
var obj = {
ID: item.ID,
ParentId: item.ParentId,
NavName: item.NavName
}
tempData.push(obj);
}
});
return tempData;
}
创建选择信息栏,首先我们移除contents-center所有的选中状态,判断当前parentId为class名称的div是否创建过,如果创建过,直接元素清除,反之创建。创建ul-li的时候同时创建切换标签页,对ul-li事件处理的时候需要判断当前点击对象是不是已经是选中状态,如果是直接执行切换操作,如果不是则对ul-li样式变化进行操作。第一步对li移除所有的选中效果;第二步暂存当前点击的class,因为待会将创建一个"selected"class名称;第三步获取点击的对象数据 对应的标签页及内容进行移除操作;第四步数据查找,如果存在继续递归创建,反之slideUp();具体代码实现如下:
var contents = $("." + linkageClass + " .contents-center");
$(contents).find("div").removeClass("actived");//移除所有的选中状态
var boxClassName = pid == "-1" ? "firstPage" : pid;
var length = 0;//判断当前的pid是否创建过,如果是创建过,直接元素清除
contents.find("div").each(function () {
if ($(this).hasClass(boxClassName))
length++;
})
var box;//定义变量存储
//#region 内容容器判断
if (length > 0) {
box = $("." + linkageClass + " .contents-center ." + boxClassName);
box.empty();
} else {//未创建
//#region 切换标签页
// 创建切换标签页
var spanHtml = $("请选择")
$("." + linkageClass + " .contents-top").append(spanHtml);
//切换标签页事件绑定
spanHtml.bind("click", function () {
$("." + linkageClass + " .contents-center").find("div").removeClass("actived");
var clsName = $(this).data("selectid");
$("." + linkageClass + " .contents-center ").find("div").each(function () {
if ($(this).hasClass(clsName))
$(this).addClass("actived");
})
$(this).parent().find("span").removeClass("selected");
$(this).addClass("selected");
})
//#endregion
//#region 创建内容容器
boxClassName += " actived";
box = $("")
$(contents).append(box);
//#endregion
}
//#endregion
//#region 创建ul-li及事件绑定处理
var ulHtml = $("
");
box.append(ulHtml);
$(arr).each(function (index, item) {
var liHtml = $("" + item.NavName + " ");
ulHtml.append(liHtml);
//绑定点击事件
liHtml.bind("click", function () {
var ParentId = $(this).data("parentid");
var spanClass = ParentId == "-1" ? "firstPage" : ParentId;//获取点击对象的class
//#region 当前点击对象为已经创建对象
if (!$(this).hasClass("selected")) {//当前点击的对象不是已经选中的元素
//#region 对ul-li样式变化进行操作
$(this).parent().find("li").removeClass("selected");//移除所有的选中效果
var classId = $(this).attr("class");//暂存当前点击的class,因为待会将创建一个"selected"class名称
$(this).addClass("selected");
//#endregion
//#region 获取点击的对象数据 对应的标签页及内容进行移除操作
$("." + linkageClass + " .contents-top ." + spanClass).text($(this).text());
$("." + linkageClass + " .contents-top ." + spanClass).data("id", classId);
$("." + linkageClass + " .contents-top ." + spanClass).nextAll().remove();//将当前元素后面的所有元素进行移除
$(this).parent().parent().nextAll().remove();//移除整个div
//#endregion
//#region 数据查找,如果存在继续递归创建
var tempData = self.selectEquativeData(classId);//查找当前点击对象下属所有数据
if (tempData != undefined && tempData.length > 0) {
$("." + linkageClass + " .contents-top ." + spanClass).removeClass("selected");
self.createHtml(linkageClass, tempData, classId);
} else {
var selectText = "";
$("." + linkageClass + " .contents-top").find("span").each(function () {
if ($(this).text().trim() != "请选择")
selectText += $(this).text().trim() + " ";
})
$(_config.containerClass).val(selectText);
if ($("." + linkageClass + " .contents-top .selected").text() == "请选择")
$(_config.containerClass).data("id", $("." + linkageClass + " .contents-top .selected").prev().data("id"));
else
$(_config.containerClass).data("id", $("." + linkageClass + " .contents-top .selected").data("id"));
$("." + linkageClass).slideUp(300);
}
//#endregion
}
//#endregion
//#region 点击的是已经选中的元素 直接执行切换的操作
else {
$("." + linkageClass + " .contents-top ." + spanClass).next().click();
}
//#endregion
})
})
//#endregion
事件绑定分为两部分第一部分是对input框进行判断,第一部分是对我们创建的元素进行绑定。为了让我们创建的选择框实时贴合在input的下方且宽度与父容器保持一致,我们注册了窗体改变大小事件。当窗体改变的时候我们更改新建div的left、top以及width,由于我们创建的信息选择框会存在一点的误差,因此我们创建了以及参数方便我们界面开发者调整解决误差。这三个参数分别是:isConsiderParent默认值为false(是否需要考虑点击对象的父容器,通常用于计算宽度)、isAddWidth默认值为false(是否需要给对应的下拉信息框追加宽度)、addWith默认值为0(追加的宽度大小,当isAddWidth为true时才生效)。
调用方法:
new Linkage({
arr: jsonData,//数据源
containerClass: ".input-belong",//文本输入框
isConsiderParent: false,//由于默认值为false 该参数可不写
isAddWidth: false,//由于默认值为false 该参数可不写
addWith: 0,//当isAddWidth为false时,该值可不传递
});
//由于默认值为false 该参数可不写
addWith: 0,//当isAddWidth为false时,该值可不传递
});
至此我们的功能已经介绍完毕。完整代码下载地址