用jQuery仿京东收货地址

今天我们要讲的是如何利用jQuery仿京东收获地址,我们涉及的主要知识点为数组遍历,函数封装。按照惯例,我们在讲功能实现之前,先看看我们的效果展示图,图一为信息接收栏,图二为点击信息接收栏下拉选择,该结构类似于combobox。

图1 信息接收栏
图2 信息选择栏

结构分为两部分,文本选择框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是否是赋值的同时是否是点击触发的,然后执行上述操作,这边不再赘述,直接贴代码。

扫描二维码关注公众号,回复: 1671183 查看本文章
 //#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 = $('<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;
    }

创建选择信息栏,首先我们移除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 = $("<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
事件绑定分为两部分第一部分是对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时,该值可不传递
        });
至此我们的功能已经介绍完毕。 完整代码下载地址

猜你喜欢

转载自blog.csdn.net/qq_21726707/article/details/80399957