好久都没有写东西了,现在干的活都是重复和简单的活,能总结出来的东西还是不太多。

    目前做电商平台,其中用到选择地址的控件,不过不是自己写的,最近花时间重新自己做一个,目前就先吧功能样式,功能完成第一版,之后再进行优化和升级,最终做成组件或者是插件形式,调用只需要几行代码去完成,不过这些都是后话了,目前显示完成基本的样子和功能。这一篇文章就当做写东西的一个笔记,记录一个过程。

    先放上出来的效果

1060移动版bios_html

    看效果图挺简单的,因为懒。就用了jquery加js,方法什么的也没考虑性能啥的,就初步先做一个出来。后面再慢慢一步步优化。

    不愿意往下看的有兴趣的可以直接看这里 githubd selectAddress-v1.0地址

    接下来我们进入正题!有什么问题可以留言跟我讨论,这个功能不是很复杂,只是作为粗糙版就没有考虑很多。

第一步:构思功能样子,先有概念!

    首先至少得有想法,做成什么样子,这个很重要,要是会点设计,可以自己先做个大概的图或者原型,既然之后要做成组件,首先页面上就得有个按钮,然后弹出选择地址的弹框,页面上得有一个显示选择完地址的标签。

    弹框出来之后,得有几个部分,一个是顶部标题,叉叉按钮用来隐藏,接下来是每级选择完的地址显示在中间,然后就是下面的每级地址列表,并且列表中若有选中的地址则标红,这些都是最基本的。

    我这边做的地址目前只有四级地址,不会有更深层次的地址,所以只有四列。构思好了,大概样子就是下图。

1060移动版bios_jquery_02

1060移动版bios_css_03

第二步:构思完之后就是构造页面

    构造页面算是最简单的部分了,就是写html,css了。写成上图的样子。

    因为四级地址列表考虑到多次切换留有缓存的问题,没有做成每切换一次就请求一次,而是做成四个列表,通过隐藏显示的方式来更方便的操作。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=0,minimum-scale=1, maximum-scale=1"/>
    <link rel="stylesheet" href="address.css">
    <script src="jquery-3.2.1.min.js"></script>
</head>
<body>
<div class="btn" onclick="show()">点击选择地址</div>

<div class="show_address" id="product_address_show" onclick="show()">北京市,北京市,朝阳区,朝外街道</div>

<div id="yls_address_choose" style="display: none">
    <div class="yls_address_bg" onclick="hide()"></div>
    <div class="yls_address_main">
        <div class="yls_address_pop_top">
            <div class="yls_address_pop_title">请选择地址</div>
            <div class="yls_address_pop_cancel" onclick="hide()"></div>
        </div>
        <div class="yls_address_pop_main">
            <div class="yls_address_product">
                <div class="yls_address_select" id="yls_address_select">
                    <div class="yls_address_top_address jdshop_alignment_middle">
                        <div id="yls_top_address_1">请选择</div>
                        <div id="yls_top_address_2"></div>
                        <div id="yls_top_address_3"></div>
                        <div id="yls_top_address_4"></div>
                    </div>
                    <div class="yls_address" id="yls_address_1"></div>
                    <div class="yls_address" id="yls_address_2"></div>
                    <div class="yls_address" id="yls_address_3"></div>
                    <div class="yls_address" id="yls_address_4"></div>
                </div>
            </div>
        </div>
    </div>
</div>
</body>
</html>

    这个html还是一目了然的,很简单,下面是css样式。

body, html {
    -webkit-user-select: none;
    user-select: none
}
html {
    -webkit-text-size-adjust: 100%
}
body {
    line-height: 1.6;
    background-color: #f5f5f9;
    color: #4a4a4a;
    font-size: 14px;
    font-family: Arial, '微软雅黑', Helvetica Neue, Helvetica, sans-serif;
    -webkit-overflow-scrolling: touch;
    overflow-scrolling: touch
}
* {
    margin: 0;
    padding: 0
}
a img {
    border: 0
}
a {
    text-decoration: none;
    -webkit-tap-highlight-color: transparent;
    -webkit-appearance: none
}
@font-face {
    font-weight: 400;
    font-style: normal;
    font-size: 14px;
    font-family: Arial, '微软雅黑', Helvetica Neue, Helvetica, sans-serif
}
input, textarea {
    border: 0;
    outline: 0;
    -webkit-appearance: none;
    -webkit-tap-highlight-color: transparent;
    font-size: inherit;
    color: inherit
}

/*点击选择按钮*/
.btn{
    margin: 0 auto;
    width: calc(50%);
    height: 30px;
    margin-top: 100px;
    background-color: lightcyan;
    line-height: 30px;
    text-align: center;
    font-size: 14px;
}
.alignment_middle{
    -webkit-box-align: center;
    -webkit-align-items: center;
    align-items: center;
    -webkit-box-pack: start;
    -webkit-justify-content: flex-start;
    justify-content: flex-start;
    display: -webkit-box;
    display: -webkit-flex;
    display: flex;
}
/*显示文字*/
.show_address{
    margin: 0 auto;
    margin-top: 10px;
    width: 80%;
    height: 30px;
    background-color: #fff;
    text-align: center;
    line-height: 30px;
    font-size: 14px;
}

/*背景*/
.yls_address_bg {
    position: fixed;
    top: 0;
    left: 0;
    height: 100%;
    width: 100%;
    background: #000;
    z-index: 500;
    opacity: .3;
    -webkit-transition: opacity .3s;
    transition: opacity .3s;
    touch-action: none
}
/*主弹框*/
.yls_address_main {
    position: fixed;
    left: 0;
    width: 100%;
    bottom: 0;
    background: #fff;
    border-radius: 10px 10px 0 0;
    box-shadow: 0 0 3px #e9e9e9;
    -webkit-transform: translate3d(0, 120%, 0);
    transform: translate3d(0, 120%, 0);
    -webkit-transition: -webkit-transform .3s;
    transition: -webkit-transform .3s;
    transition: transform .3s;
    transition: transform .3s, -webkit-transform .3s;
    z-index: 1001;
    transform: translate3d(0, 0, 0);
}
.yls_address_pop_top {
    text-align: center;
    height: 50px;
    margin-bottom: 5px;
}
.yls_address_pop_title {
    height: 100%;
    line-height: 50px;
}
.yls_address_pop_cancel {
    position: absolute;
    right: 0;
    top: 0;
    width: 50px;
    height: 50px;
}
.yls_address_pop_cancel::before, .yls_address_pop_cancel::after {
    content: '';
    width: 16px;
    height: 1px;
    background: #000;
    display: block;
    position: absolute;
    right: 10px;
    top: 25px;
}
.yls_address_pop_cancel::before {
    transform: rotate(45deg); /*进行旋转*/
}
.yls_address_pop_cancel::after {
    transform: rotate(-45deg);
}
.yls_address_pop_main {

}
.yls_address_product{

}
.yls_address_select{
    height: calc(60vh);
    width: 100%;
    position: relative;
    overflow: hidden;
}
.yls_address_top_address{
    font-size: 12px;
    height: 35px;
    overflow: hidden;
    border-bottom: 1px solid #ddd;
}
.yls_address_top_address>div{
    padding: 5px 5px;
    margin: 0 5px;
    white-space: nowrap;
}
.yls_address_top_address>div.show{
    color: #c91623;
    border-bottom: #c91623 1px solid;
}
.yls_address{
    position: absolute;
    left: 0;
    top: 45px;
    overflow: auto;
    width: 100%;
    height: calc(60vh - 35px);
    -webkit-transform: translate3d(-100%,0,0);
    transform: translate3d(-100%,0,0);
    -webkit-transition: -webkit-transform .3s .2s;
    transition: -webkit-transform .3s .2s;
    transition: transform .3s .2s;
    transition: transform .3s .2s,-webkit-transform .3s .2s;
}
.yls_address p{
    padding: 8px 10px;
    font-size: 14px;
}
.yls_address p.p_show {
    position: relative;
    color: #c91623;
}
.yls_address.show {
    -webkit-transform: translate3d(0,0,0);
    transform: translate3d(0,0,0);
}

    css上半部分是一些基本的初始化浏览器样式,下面部分就是地址选择的样式,目前还没加入动态效果,之后优化部分考虑做进去。不过这部分的样式完全可以自己按照自己的喜好做,想做成什么样子就做成什么样子的。

第三步 :逻辑部分

    这个部分算是最重要的,不过在此重申一遍,我做的只是粗糙的版本,逻辑部分是没有经过任何优化的,这个大家可以按照自己优化的方式进行优化,后期我会做成插件形式再写一篇。?希望看到这篇文章的大家不要嘲笑我写的粗糙,毕竟我写这个也只是给新手看看,不要嫌弃。

1.做基本的变量声明工作

/*赋初始值*/
    var startId = [1804,1805,1829,1831];
    var startName = ['北京市','北京市','朝阳区','朝外街道'];

    //最终获取到的值
    var returnStartId = [];
    var returnStartName = [];

2.做弹出弹框的弹出隐藏方法

/*显示地址选择,隐藏地址选择*/
    function show() {
        document.getElementById("yls_address_choose").style.display="";//显示
        var url = 'http://*********/address/areas?areaId=';//这边写你请求接口的url
        initAddress(url,startId,startName);//初始化工作的方法
    }

    /*隐藏地址选择*/
    function hide() {
    //点击隐藏的时候循环将列表所有的show样式去除,重新添加到最后一列上
        for(var i = 1;i < 5;i++){
            var ad = 'yls_top_address_' + i;
            var adid = 'yls_address_' + i;
            $("#"+ad).removeClass('show');
            $("#"+adid).removeClass('show');
            $("#"+ad).show();
            $("#"+adid).show();
        }
        document.getElementById("yls_address_choose").style.display="none";//显示
        //点击隐藏了,就把最终值还原成初始值
        returnStartId = startId;
        returnStartName = startName;
    }

3.做弹出弹框的初始化工作

//初始化方法
    function initAddress(url,startId,startName) {
        //将我们最后需要获取的值初始化成初始值
        returnStartId = startId;
        returnStartName = startName;
        //初始化请求areaid=0的数据,我用的接口areaid=0的时候是请求省级地址
        request(url,0,1);
    }

     //初始化请求数据部分,
    //thisNum 为areaid变化数值 url是请求url, addressNum是每个address是第几个
    function request(url,thisNum,addressNum){
        var addressUrl = url+ thisNum;
        $.ajax({
            type: 'get',
            url: addressUrl,
            dataType: "json",
            success: function (res) {
                //无常规错误,根绝data数据做具体的业务判断显示
                console.log(res.result);
                //初始化的渲染html
                setHtml(res.result,thisNum,addressNum);
                //循环请求下级地址
                if(addressNum < startId.length){
                    request(url,startId[addressNum-1],addressNum+1);
                    addressNum += 1;
                }
            },
            error: function erryFunction(err) {
                //网络请求错误
                console.log(err);
                console.log('读取失败')
            }
        });
    }

    //初始化渲染页面
    function setHtml(data,thisNum,addressNum) {
        var addressId = 'yls_address_' + addressNum;
        var topAddress = 'yls_top_address_' + addressNum;
        if(addressNum == startId.length){
            $("#"+addressId).addClass('show');
            $("#"+topAddress).addClass('show');
        }
        $("#"+topAddress).text(startName[addressNum-1]);

        //初始化之后选中的地址顶部tab进行添加点击方法,每次点击会做什么事情
        document.getElementById(topAddress).addEventListener("click",function () {
            var a = parseInt(topAddress.substr(topAddress.length-1,1));
            console.log(a);
            for(var i = 1;i < 5;i++){
                var ad = 'yls_top_address_' + i;
                var adid = 'yls_address_' + i;
                if(i != a){
                    $("#"+ad).removeClass('show');
                    $("#"+adid).removeClass('show');
                    if(i > a){
                        $("#"+ad).hide();
                    }
                }else{
                    $("#"+ad).addClass('show');
                    $("#"+adid).addClass('show');
                }
            }

            //tab上选中地址点击的时候,最终选择地址数组数据也要相应的变化
            returnStartId = returnStartId.slice(0,addressNum);
            returnStartName = returnStartName.slice(0,addressNum);
           
        },false);
        $("#"+addressId).empty();//清空当前列表的html
        var html = '';
        //拼接html,为地址列表中每一个地址添加点击方法
        if(data){
            for(var i = 0;i < data.length;i++){
                if(startName[addressNum-1] == data[i].name){
                    html += '<p class="p_show" id="add_'+data[i].areaId+'" onclick="chooseDetail('+data[i].areaId+','+addressNum+',"'+data[i].name+'")">'+data[i].name+'</p>';
                }else{
                    html += '<p id="add_'+data[i].areaId+'" onclick="chooseDetail('+data[i].areaId+','+addressNum+',"'+data[i].name+'")">'+data[i].name+'</p>';
                }
            }
        }
        //添加html到列表上渲染页面
        $("#"+addressId).append(html);
    }

4.每次点选地址之后的具体逻辑

//点击列表中详细地址的方法
    function chooseDetail(areaId,addressNum,name) {
        var url = 'http://******/address/areas?areaId=';
        var adid = 'add_'+areaId;
        var adid1 = 'yls_address_' + addressNum;
        //选中的数据加上标红显示
        $("#"+adid1+'>p').removeClass('p_show');
        $("#"+adid).addClass('p_show');

        //点击具体地址之后,将最终选择地址数组中相应位置的数据替换掉
        returnStartId[addressNum-1] = areaId;
        returnStartName[addressNum-1] = name;
        
        //判断是否是第四级地址,如果是第四级地址,把获取到的数据渲染到页面上,并且隐藏弹框
        //如果是第四级地址,发送请求获取下一级的数据进行渲染页面
        if(addressNum == 4){
            var topAddress1 = 'yls_top_address_' + addressNum;
            $("#"+topAddress1).text(name);
            $("#product_address_show").text(returnStartName);
            hide();
        }else{
            newrequest(url,areaId,addressNum,name);
        }
    }


    //点击列表里具体地址,发送新的请求获取下一级数据
    //thisNum 为areaid变化数值 url是请求url, addressNum是每个address是第几个,name是你选择的地址具体名称
    function newrequest(url,thisNum,addressNum,name){
        var addressUrl = url+ thisNum;
        $.ajax({
            type: 'get',
            url: addressUrl,
            dataType: "json",
            success: function (res) {
                //无常规错误,根绝data数据做具体的业务判断显示
                console.log(res.result);
                //获取到数据之后渲染下一级列表
                setHtml2(res.result,thisNum,addressNum,name);
            },
            error: function erryFunction(err) {
                //网络请求错误
                console.log(err);
                console.log('读取失败')
            }
        });

    }

    //选择详细地址请求获取数据之后渲染方法
    function setHtml2(data,thisNum,addressNum,name) {
        //获取到相应的div
        var adid1 = 'yls_address_' + addressNum;
        var adid2 = 'yls_address_' + (addressNum+1);
        var topAddress1 = 'yls_top_address_' + addressNum;
        var topAddress2 = 'yls_top_address_' + (addressNum+1);

        //清空相应的下一级div和下一级tab上选中的地址,填入新的数据
        $("#"+topAddress2).empty();
        $("#"+adid2).empty();
        $("#"+topAddress2).addClass('show');
        $("#"+topAddress2).show();
        $("#"+topAddress1).removeClass('show');
        $("#"+adid2).addClass('show');
        $("#"+adid1).removeClass('show');
        $("#"+topAddress2).text('请选择');
        $("#"+topAddress1).text(name);
        var html = '';
        if(data){
            //拼接html,并且添加上新的详细地址选择方法
            for(var i = 0;i < data.length;i++){
                html += '<p id="add_'+data[i].areaId+'" onclick="chooseDetail('+data[i].areaId+','+(addressNum+1)+',"'+data[i].name+'")">'+data[i].name+'</p>';
            }
        }
        $("#"+adid2).append(html);
    }

    以上三步就完成了地址选择粗糙版的构造,其中用来请求数据的方法写了两个,拼接html渲染页面的方法也写了两个,这些是有点冗余的,这个部分之后可以合成一个。

    目前优化的构思是有,不过还没来得及实施,得找时间做一下,优化的思路是其中的方法先进行相应的优化,最后集成成一个对象,在页面调用的时候只要把初始化数据传入,最后加上一个回调方法,将选择完成的地址返回回来,这样整个页面也呢个简洁很多,调用方法也简单了,使用起来的话会方便很多。

    因为目前我写的vue的单页面应用,用到keepAlive,所以现有的控件总是出问题,等这两天优化完进行测试下,到时候在写一篇简单点的文章发布出来。