今天我们要讲的是如何利用jQuery仿京东收获地址,我们涉及的主要知识点为数组遍历,函数封装。按照惯例,我们在讲功能实现之前,先看看我们的效果展示图,图一为信息接收栏,图二为点击信息接收栏下拉选择,该结构类似于combobox。
结构分为两部分,文本选择框input,因为我们这边为了方便给input赋值之后能够模拟正常的点击效果,我们这边的input需要定义data-*类型数据,实例代码如下:
<input type="text" name="msg" readonly="readonly" id="msg" placeholder="请选择门店/部门/团队" data-id="854D87EFB26A04B8" data-assign="1" data-isclick="0"/>
上方的代码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
//#region 定义变量 var self = this, _config = self.config, _cache = self.cache; var _Linkage = new Date().getTime(); _Linkage = 'Y' + _Linkage; //#endregion //#region 创建div var divHtml = $('<div class="contentsMain ' + _Linkage + '">' + ' <!--顶部选择门店/部门/团队类似结构数据-->' + ' <div class="contents-top"></div>' + ' <!--筛选-->' + ' <div class="contents-center"></div>' + ' <!--底部操作-->' + ' <div class="contents-bottom">' + ' <span>点击完成即为选择完成</span>' + ' <span class="ok">完成</span>' + ' <span class="cancel">取消</span>' + ' </div>' + '</div>'); $("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; }
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 = $("<span class='" + boxClassName + " selected' data-selectid='" + boxClassName + "' data-id='-1'>请选择</span>") $("." + 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 = $("<div class='" + boxClassName + "'></div>") $(contents).append(box); //#endregion } //#endregion //#region 创建ul-li及事件绑定处理 var ulHtml = $("<ul></ul>"); box.append(ulHtml); $(arr).each(function (index, item) { var liHtml = $("<li class='" + item.ID + "' data-parentid='" + item.ParentId + "' data-index='" + index + "'>" + item.NavName + "</li>"); 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
new Linkage({
arr: jsonData,//数据源
containerClass: ".input-belong",//文本输入框
isConsiderParent: false,//由于默认值为false 该参数可不写
isAddWidth: false,//由于默认值为false 该参数可不写
addWith: 0,//当isAddWidth为false时,该值可不传递
});