用jQuery仿京东收货地址

代码优化及修正说明,新增两个功能,第一、指定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 信息接收栏

用jQuery仿京东收货地址_第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时,该值可不传递
            });

    至此我们的功能已经介绍完毕。完整代码下载地址

    你可能感兴趣的:(js,web)