zoukankan      html  css  js  c++  java
  • 地址选择控件开发--如何优雅地回答保安小哥的哲理问题?

    "你从哪里来?”

    “你要到哪里去?"

    这是保安小哥经常会问的具有哲理性的问题。在互联网的应用的开发中,也经常会用到有关地址的选择设置。不管是物流的应用,还是外卖的应用,都会要求用户设置用户所在的位置。如果让用户来输入完整的地址,一方面,输入比较慢,体验不好。另一方面,输入的地址不规范,例如:"浙江省温州市永嘉县",有些人会输入"浙江温州永嘉",有些人则会输入"浙江省永嘉县",这对服务端的数据处理、分析也带来不便。

    如何开发一个体验良好的“地址选择Web控件”,如何优雅地回答保安小哥的哲理问题?估计是广大Web应用开发人员一直在思考的问题。与其他一些标准Web控件(例如:组合框、单选框等)不同,“地址选择Web控件”具有区域特征,功能也比较综合,大部分标准的UI库都不会提供,所以,捣鼓一下还是很有必要的。

    先睹为快

    闲话少说,我们先来看看今天我们研究的控件的最终效果图(参照天猫的送货地址设置的效果):

    “地址选择Web控件”的基本组成:

    使用控件举例:

    <!--需要加载和引用的文件-->
    <link rel="stylesheet" href="css/zlbox.css" type="text/css" media="screen" />
    <script src="js/jquery-1.7.1.js"></script>
    <script src="js/jquery.zlbox.js"></script>
    
    <!--控件的HTML代码-->
    <div id="company_addr" class="zl_addressbox">
               <div class="ab_showbar tip">
                  <div class="tip_info">请选择省市区</div>
                  <div class="value_info">
                      <!--结果显示容器-->
                  </div>
               </div>
               <span class="ab_btn"></span>
               <div class="selectaddr_box">
                  <div class="ab_bar">
                     <ul>
                        <li class="sheng current">省份</li>
                        <li class="shi">城市</li>
                        <li class="qu">区县</li>
                     </ul>
                  </div>
                  <div class="ab_panel">
                    <dl>
                       <dd class="sheng current">
                          <div class="ab_group">
                            <span class="ab_shengtitle">A-G</span>
                            <ul class="ab_sheng ab_item">
                               <li>北京</li>
                               <li>广东</li>
                            </ul>
                          </div>
                          <div class="ab_group">
                            <span class="ab_shengtitle">T-Z</span>
                            <ul class="ab_sheng ab_item">
                               <li>浙江</li>
                            </ul>
                          </div>
                       </dd>
                       <dd class="shi">
                          <ul class="ab_shi ab_item">
                            <!--市区容器-->
                          </ul>
                       </dd>
                       <dd class="qu">
                          <ul class="ab_qu ab_item">
                            <!--区县容器-->
                          </ul>
                       </dd>
                    </dl>
                  </div>
               </div>
            </div>
            <!--End of ZLAddressBox-->
    
    
    <script type="text/javascript">
        //初始化地址控件
        $('#company_addr').czl_addressbox({});
    </script>

    为了讲解的方便,控制代码的篇幅,这里仅仅举例了北京、广东、浙江3个省市的“省份/城市/区县”选择,数据完整的“全国地址选择Web控件”还在捣鼓中,出货后再向诸位汇报。

    基本功能实现

    【HTML代码】

    Web控件涉及的HTML代码如下:

    <!--ZLAddressBox-->
            <div id="company_addr" class="zl_addressbox">
               <div class="ab_showbar tip">
                  <div class="tip_info">请选择省市区</div>
                  <div class="value_info">
                      <!--结果显示容器-->
                  </div>
               </div>
               <span class="ab_btn"></span>
               <div class="selectaddr_box">
                  <div class="ab_bar">
                     <ul>
                        <li class="sheng current">省份</li>
                        <li class="shi">城市</li>
                        <li class="qu">区县</li>
                     </ul>
                  </div>
                  <div class="ab_panel">
                    <dl>
                       <dd class="sheng current">
                          <div class="ab_group">
                            <span class="ab_shengtitle">A-G</span>
                            <ul class="ab_sheng ab_item">
                               <li>北京</li>
                               <li>广东</li>
                            </ul>
                          </div>
                          <div class="ab_group">
                            <span class="ab_shengtitle">T-Z</span>
                            <ul class="ab_sheng ab_item">
                               <li>浙江</li>
                            </ul>
                          </div>
                       </dd>
                       <dd class="shi">
                          <ul class="ab_shi ab_item">
                            <!--市区容器-->
                          </ul>
                       </dd>
                       <dd class="qu">
                          <ul class="ab_qu ab_item">
                            <!--区县容器-->
                          </ul>
                       </dd>
                    </dl>
                  </div>
               </div>
            </div>
            <!--End of ZLAddressBox-->

    【CSS代码】

    Web控件涉及到的CSS代码(zlbox.css)如下:

      1 /**全国地址选择控件**/
      2 .zl_addressbox{
      3     position:relative;
      4     width:370px;
      5     background-color:#F08;
      6 }
      7 .zl_addressbox .ab_showbar{
      8     width:100%;
      9     height:30px;
     10     line-height:30px;
     11     border:#A9A9A9 1px solid;
     12     background-color:#FFF;    
     13 }
     14 .zl_addressbox .ab_btn{
     15     position:absolute;
     16     right:0px;
     17     top:1px;
     18     width:30px;
     19     height:30px;
     20     background:#FFF url('img/cb_btn_down.png') no-repeat center center;
     21 }
     22 /**地址选择面板**/
     23 .zl_addressbox .selectaddr_box{
     24     display:none;
     25     position:absolute;
     26     left:0px;
     27     right:0px;
     28     top:32px;
     29     width:100%;
     30     line-height:30px;
     31     background-color:#FFF;
     32     z-index:888;
     33 }
     34 .zl_addressbox .ab_bar{
     35     width:100%;
     36     height:40px;
     37 }
     38 .zl_addressbox .ab_bar:after{
     39     clear:both;
     40     content:'';
     41     display:table;
     42 }
     43 .zl_addressbox .ab_bar li{
     44     float:left;
     45     width:122px;     /**这里如果要调整容器大小,需要同步调整**/
     46     height:40px;
     47     line-height:40px;
     48     text-align:center;
     49     background-color:#F0F0F0;
     50     border-left:#CCC 1px solid;
     51     border-bottom:transparent 1px solid ;
     52     cursor:pointer;
     53 }
     54 .zl_addressbox .ab_bar li:first-child{
     55     border-left:none;
     56 }
     57 .zl_addressbox .ab_bar li.current{
     58     color:#009AFD;
     59     background-color:#FFF;
     60     cursor:none;
     61 }
     62 .zl_addressbox .ab_panel{
     63     position:relative;
     64     width:100%;
     65 }
     66 .zl_addressbox .ab_panel dd{
     67     display:none;
     68     position:relative;
     69     width:100%;
     70 }
     71 .zl_addressbox .ab_panel dd.current{
     72     display:block;
     73 }
     74 /**省份面板中的分组**/
     75 .zl_addressbox .ab_panel dd .ab_group{
     76     position:relative;
     77     width:100%;
     78     margin-bottom:20px;
     79 }
     80 .zl_addressbox .ab_panel dd .ab_group span.ab_shengtitle{
     81     display:block;
     82     position:absolute;
     83     top:0px;
     84     width:60px;
     85     height:30px;
     86     line-height:30px;
     87     text-align:center;
     88     font-size:0.8em;
     89     color:#009AFD;
     90 }
     91 .zl_addressbox .ab_panel dd .ab_group .ab_sheng{
     92     margin-left:40px;
     93 }
     94 .zl_addressbox .ab_panel dd ul.ab_item{
     95     margin-top:10px;
     96     margin-bottom:5px;
     97 }
     98 .zl_addressbox .ab_panel dd ul.ab_item:after{
     99     clear:both;
    100     display:table;
    101     content:'';
    102 }
    103 .zl_addressbox .ab_panel dd ul.ab_item li{
    104     float:left;
    105     height:30px;
    106     line-height:30px;
    107     padding:0px 10px;
    108     margin-left:10px;
    109     cursor:pointer;
    110 } 
    111 .zl_addressbox .ab_panel dd ul.ab_item li:hover{
    112     color:#009AFD;
    113 }
    114 .zl_addressbox .ab_panel dd ul.ab_item li.current{
    115     border-radius:6px;
    116     color:#FFF;
    117     background-color:#009AFD;
    118 }
    119 .zl_addressbox.selected .selectaddr_box{
    120     display:block;
    121     border-left:1px #CCCCCC solid;
    122     border-right:1px #CCCCCC solid;
    123     border-bottom:1px #CCCCCC solid;
    124     padding-bottom:10px;
    125 }
    126 .zl_addressbox.selected  .ab_btn{
    127     background:#FFF url('img/cb_btn_up.png') no-repeat center center;
    128 }
    129 /**提示状态**/
    130 .zl_addressbox .ab_showbar .tip_info{
    131     display:none;
    132     width:90%;
    133     padding-left:5px;
    134     color:#CCCCCC;
    135 }
    136 .zl_addressbox .ab_showbar .value_info{
    137     display:block;
    138     width:90%;
    139     padding-left:5px;
    140 }
    141 .zl_addressbox .ab_showbar .value_info span.sep{
    142     color:#CCC;
    143     font-size:0.8em;
    144 }
    145 
    146 .zl_addressbox .ab_showbar.tip .value_info{
    147     display:none;
    148 }
    149 .zl_addressbox .ab_showbar.tip .tip_info{
    150     display:block;
    151 }

    结合我们的功能诉求,一级前面介绍的控件的组成,将HTML代码与CSS代码对照起来理解应该比较好理解,所以,关于控件涉及到的布局、样式等细节,就不展开赘述,我们把重点放到JavaScript的代码部分。开发Web控件的基本套路与前一篇博文:ZLComboBox自定义控件开发详解  类似,本文就着重讲解控件的业务需求以及与ZLComboBox控件开发不一样的地方。

    JavaScript--闭包实现

    与ZLComboBox控件不同,本文我们采用闭包的方式来构建我们的控件对象,jQuery插件的基本代码如下:

    $.fn.czl_addressbox = function( options )
       {
           this.each( function()  
           {
              var instance = $.data( this , 'czl_addressbox' );
              
              if( !instance )
              {
                $.data( this, 'czl_addressbox' , $.createAddressBox( options , this ) );  
              
           });//end of each
           
           return this;  //支持链式操作
       };

    控件对象通过createAddressBox()来返回一个控件对象,下面我们重点来分析一下createAddressBox()的基本组成。

    整个控件对象基于地址的'三级'目录树,以id来进行各级行政单位的标识与处理。

    整个控件对外的接口:

       1> 创建控件时,可以传入一个对象字面量(options),用于设置默认选中的地址。

       2> 返回当前选中地址的对象字面量:get_addr_obj()。

       3> 传入一个地址对象字面量:set_addr_obj( new_addr_obj )。

    业务流程分析:

    1> 默认情况下,控件显示'请选择省市区'的提示信息。

    2> 单击控件'地址栏',显示下拉的'地址选择面板';再次单击'地址栏',隐藏下拉的'地址选择面板';下拉的'地址选择面板'反映当前的选择状态。

    3> 在'省份'面板中选中一个'省级单位'后,自动显示选中省级单位下的'市级'行政区。

         在'城市'面板中选中一个'市级单位'后,自动显示选中市级单位下的'区级'行政区。

         在'区县'面板中选中一个'区级单位'后,自动隐藏'地址选择面板'。

         所有的选择结果,都会实时在控件'地址栏'中显示出来。

    4> 只有选中了'省级'单位之后,才能单击'市级'单位的页签,

         只有选中了'市级'单位之后,才能单击'区级'单位的页签。

    5> 当更改了某一级别的选择结果之后,将它的下一级单位的选择清空。

         例如:已经选择了"广东/深圳",这时候单击"省份"面板中的"浙江",则原来"城市面板"中的内容被清空,重新设置为"浙江"所包含的城市。

    综合业务需求,闭包中的成员可以包括:

    私有属性

    • 保存了'三级地址信息'的对象字面量:addr_box_data
    • 用于记录选择结果的对象字面量:addr_obj
    • 一些HTML元素的引用等:例如;地址栏元素ab_showbar

    私有函数

    •  选中某个'省级单位','市级单位'和'区级单位'之后的响应函数。

            _selectShengId( sheng_index )

            _selectShiId( shi_index )

            _selectQuId( qu_index )

    • 更新地址栏信息的函数:_updateAddrValue( )
    • 创建控件时,要执行的初始化函数:_init( options )
    • 相关控件的注册函数:_loadEvents( ),这个函数应该在_init()中被调用。

    因为整个架构都是基于id来创建,而从界面呈现来看,我们更习惯于'地址名称',

    例如:"浙江省"的id是多少?不去翻阅addr_box_data,我们是回答不上来的。

    所以,增加一个辅助函数:getShengIDFromName( sheng_name )

    控件对外提供的接口函数

    • 获得当前选中的地址信息的对象字面量:get_addr_obj()
    • 设置默认选中的地址信息:set_addr_obj:function( a_obj )

    控件提供的HTML接口

    每次更新addr_obj的时候,同步将地址的信息更新到 HTML 元素的data属性中,名称为:addr_value。

    例如:如果依次选中了:'浙江>杭州>滨江',那么,addr_value的值为:'浙江_杭州_滨江'。

    这种方式的好处是:在应用中直接用$('.zl_addressbox').czl_addressbox({}); 就把所有的addressbox控件都初始化了。后续要取值时,直接从对应元素的data属性中获取即可,而不用去分析如何调用控件对象。

    综合以上分析,createAddressBox()代码的基本框架如下:

    $.createAddressBox = function( options , element ){
            //私有成员声明
            var addr_box = $( element );
            //相关的HTML组件
            //......
            
            //省份的选择控件
            var ab_sheng_item = addr_box.find( '.ab_sheng>li' );
            
            //保存了'三级地址信息'的对象字面量
            var addr_box_data = {} ;       
            
            //用于记录选择结果的对象字面量
                   var addr_obj = {};
            
            //私有函数声明
            var _init = function( options ){
                //加载事件注册
                _loadEvents();

    addr_box.data( 'addr_value' , '' );
    //...
            };
            
            //依据a_obj的值初始化控件的状态
            var _init_addr_obj = function( a_obj ){
                //......
            }
            
            //更新地址栏中的值
            var _updateAddrValue = function(){
                //......
            };
            
            //选择序号为sheng_id的省份之后的响应事件
            var _selectShengId = function( sheng_index ){
                //......
            };
            
            //选择序号为shi_index的城市之后的响应事件
            var _selectShiId = function( shi_index ){
                 //......
            };
            //选择序号为qu_index的区县之后的响应事件
            var _selectQuId = function( qu_index ){
                //......
            };
            
            //根据省份的名称,选择对应的id
            var getShengIDFromName = function( sheng_name ){
                //......
            }
            
            //注册事件
            var _loadEvents = function(){
                //......    
            };
    
            //执行初始化函数
            _init( options );
    
            //创建对象
            var that = {
                get_addr_obj:function(){
                    return addr_obj;
                },
                set_addr_obj:function( a_obj ){
                    _init_addr_obj( a_obj );
                    return ;
                }
            };
            
            
            //返回对象
            return that ;
       }        

    JavaScript--事件冒泡

    重点看一下'市级'面板和'区级'面板相关选项的单击事件处理。根据前面的分析,我们发现这两个面板的选项,是动态变化的,那我们应该如何处理呢?

    这让我们想到了事件的'冒泡'机制,只要注册相关容器的单击事件,当选中某个选项时,自然会冒泡到'容器'的处理函数那里。不难,直接上代码:

            //注册事件
            var _loadEvents = function(){
                //......
            
                //单击'市'下面的选项的响应事件
                ab_shi_databox.on( 'click' , function( event ){
                    //阻止事件冒泡
                    event.stopPropagation();
                    var $target = $( event.target );
                    if( $target.attr( 'tagName' ) === 'li' ){
                        var shi_item = $target ;
                        //如果当前的'市'选项就是选中的选项,则不响应事件,直接返回
                        if( shi_item.hasClass('current') ){
                            return ;
                        }
                        //获得城市对应的id的值
                        var shi_id = shi_item.index();
                        //响应选中城市之后的响应函数
                        _selectShiId( shi_id );
                        //标记当前选中的"城市"
                        shi_item.addClass('current').siblings().removeClass( 'current' );
                    }//如果是子元素li上触发的事件
                    return ;
                });
                
                //单击'区'下面的选项的响应事件
                ab_qu_databox.on( 'click' , function( event ){
                    //阻止事件冒泡
                    event.stopPropagation();
                    var $target = $( event.target );
                    if( $target.attr( 'tagName' ) === 'li' ){
                        var qu_item = $target ;
                        if( qu_item.hasClass('current') ){
                            return ;
                        }
                        //获得区县对应的id的值
                        var qu_id = qu_item.index();
                        //响应选中区县之后的响应函数
                        _selectQuId( qu_id );
                        qu_item.addClass('current').siblings().removeClass( 'current' );
                        return ;
                    }
                });
            
            };
                    

    优化控件:

     到目前为止,我们的控件已经能够工作了,但是,还有一些可以优化地方,我们一起来分析一下。

    JavaScript--单例模式

    每次我们使用控件的时候,都要执行createAddressBox()创建一个对象,根据闭包的特性,每次创建时,闭包中的私有属性成员都会被单独创建,作为一个完整的工作区占据内存空间。 我们注意到包含"三级地址信息"的addr_box_data也是一个'私有属性',而这个对象字面量所占的内存空间非常大,如果每次调用createAddressBox()都占用一段大内存空间,从性能上来看,显然不是一个好的设计。

    因为addr_box_data的内容是固定的,所以,可以把"三级地址信息"保存到一个单独的全局空间中,

    而仅仅在createAddressBox()中引用这个全局空间即可。

       优化方式如下:

       a. 创建一个全局变量:$.zl_addr_box_data。

       b. 在createAddressBox中引用这个全局变量。

    相关的代码示例如下:

    $.createAddressBox = function( options , element ){
          //......
          var addr_box_data = $.zl_addr_box_data;         //引用的全局变量
          //......
    }    
    
    
    //保留有全国地址信息的全局变量
       $.zl_addr_box_data = (function( ){
          var that = {
    '0':['北京','浙江','广东'],
    '0_0':['北京'],
    '0_0_0':['东城','西城','崇文','宣武','朝阳','丰台','石景山','海淀','门头沟','房山','通州','顺义','昌平','大兴','怀柔','平谷','密云','延庆'],
    '0_1':['杭州','宁波','温州','嘉兴','湖州','绍兴','金华','衢州','舟山','台州','丽水'],
    '0_1_0':['上城','下城','江干','拱墅','西湖','滨江','萧山','余杭','桐庐','淳安','建德','富阳','临安'],
    '0_1_1':['海曙','江东','江北','北仑','镇海','鄞州','象山','宁海','余姚','慈溪','奉化'],
    '0_1_2':['鹿城','龙湾','瓯海','洞头','永嘉','平阳','苍南','文成','泰顺','瑞安','乐清'],
    '0_1_3':['秀城','嘉善','海盐','海宁','平湖','桐乡'],
    '0_1_4':['吴兴','南浔','德清','长兴','安吉'],
    '0_1_5':['越城','绍兴','新昌','诸暨','上虞','嵊州'],
    '0_1_6':['婺城','金东','武义','浦江','磐安','兰溪','义乌','东阳','永康'],
    '0_1_7':['柯城','衢江','常山','开化','龙游','江山'],
    '0_1_8':['定海','普陀','岱山','嵊泗'],
    '0_1_9':['椒江','黄岩','路桥','玉环','三门','天台','仙居','温岭','临海'],
    '0_1_10':['莲都','青田','缙云','遂昌','松阳','云和','庆元','景宁','龙泉'],
    '0_2':['广州','韶关','深圳','珠海','汕头','佛山','江门','湛江','茂名','肇庆','惠州','梅州','汕尾','河源','阳江','清远','东莞','中山','潮州','揭阳','云浮'],
    '0_2_0':['荔湾','越秀','海珠','天河','白云','黄埔','番禺','花都','南沙','萝岗','增城','从化'],
    '0_2_1':['武江','浈江','曲江','始兴','仁化','翁源','乳源','新丰','乐昌','南雄'],
    '0_2_2':['罗湖','福田','南山','宝安','龙岗','盐田','光明新区','坪山新区','龙华新区','大鹏新区'],
    '0_2_3':['香洲','斗门','金湾'],
    '0_2_4':['龙湖','金平','濠江','潮阳','潮南','澄海','南澳'],
    '0_2_5':['禅城','南海','顺德','三水','高明'],
    '0_2_6':['蓬江','江海','新会','台山','开平','鹤山','恩平'],
    '0_2_7':['赤坎','霞山','坡头','麻章','遂溪','徐闻','廉江','雷州','吴川'],
    '0_2_8':['茂南','茂港','电白','高州','化州','信宜'],
    '0_2_9':['端州','鼎湖','广宁','怀集','封开','德庆','高要','四会'],
    '0_2_10':['惠城','惠阳','博罗','惠东','龙门'],
    '0_2_11':['梅江','梅县','大埔','丰顺','五华','平远','蕉岭','兴宁'],
    '0_2_12':['城区','海丰','陆河','陆丰'],
    '0_2_13':['源城','紫金','龙川','连平','和平','东源'],
    '0_2_14':['江城','阳西','阳东','阳春'],
    '0_2_15':['清城','佛冈','阳山','连山','连南','清新','英德','连州'],
    '0_2_16':['东莞'],
    '0_2_17':['中山'],
    '0_2_18':['湘桥','潮安','饶平'],
    '0_2_19':['榕城','揭东','揭西','惠来','普宁'],
    '0_2_20':['云城','新兴','郁南','云安','罗定'],
          };      
          return that ;
       })();

    特殊情况处理:

    我们注意到,像北京、上海、天津这些直辖市,虽然是省级单位,但是它的下面直接就是包含了各个区的"区级单位",为了保证'三级地址结构'的一致性,我们给它补上'市级单位',名称也是北京市。但是,在用户选择的时候,如果还要按"北京 > 北京 > 海淀"这样的方式,就显得太麻烦了,所以,我们做如下的优化:

    a. 当用户选中一个'省级单位'时,如果它下面只有一个'市级单位',就直接跳过这个'市级单位'。

          比如:用户选中'北京'之后,就直接显示'朝阳'、'海淀'等'区级单位'

    b. 优化显示结果:

          如果发现用户选中的'省级单位'与'市级单位'的名称一样,就只显示省级单位。

          例如:北京/石景山

    优化的相关代码如下:

       $.createAddressBox = function( options , element ){
                 //......
                 //选择序号为sheng_id的省份之后的响应事件
             var _selectShengId = function( sheng_index ){
                    //......
                    //判断是否是'北京'、'上海'、天津、重庆等直辖市
                    var shi_list_id = '0_'+sheng_index;
                    //这里处理需要跳过'市'这一级别的处理
                    if( addr_box_data[shi_list_id].length === 1 ){
                        //更新地址的值
                        addr_obj.sheng = sheng_name;
                        addr_obj.shi = '' + addr_box_data['0_'+sheng_index+'_0'] ;
                        addr_obj.qu = '' ;
                        addr_obj.sheng_id = ''+sheng_index;
                        addr_obj.shi_id = '0' ;
                        addr_obj.qu_id = '' ;
                                    
                        //更新地址栏中的值
                        _updateAddrValue( );
                        //触发选中了区的选项卡
                        _selectShiId( 0 );
                    }
                   //......
            }
    
            //更新地址栏中的值
            var _updateAddrValue = function(){
                  //......
                      if( addr_obj.shi !== '' &&  addr_obj.shi === addr_obj.sheng){
                        if( addr_obj.qu !== '' ){
                            ab_vauleinfo.append( '<span class="sep">/</span><span>'+addr_obj.qu+'</span>' );
                        }
    }
    //...... } }

    小节:

    本文所论述的是地址控件的业务原理,在真正的生产环境中使用时,也许还需要结合控件的使用场景,

    公司的业务规范来进行优化,比如思考以下几个方面:

    a. 安全性:我们当前的设计,是将'三级地址信息'直接放到JS脚本中,在实际的业务场景中,你或许需要将这个信息放到'云端',用到时才从'云端'动态加载。

    这样做的另外一个好处就是,当信息发生变化时(例如:行政区域发生变更),只要更新'云端'的数据就可以了。

    b. 移动化:我们当前的设计,是将'地址选择面板'进行'悬挂式'设计,即:显示'地址选择面板'时,并不会影响原来的布局流。这个在某些使用场景时可能会出现问题,例如:如果这个控件放置在页面的底部,就不能完整的显示整个'地址选择面板',这个在'移动化'应用设计中会遇到比较多,这时候,也许将它修改成'抽屉式'设计更合理一些。

    '悬挂式'设计和'抽屉式'设计的示意图如下:

    抽屉式'设计的样式如何写?就留作练习,让大家练练手吧。

    c. 兼容性:在前端设计中,因为您可能会遇到各种浏览器,所以兼容性也是需要大家考量的问题,例如事件的注册、事件对象访问等,不过由于我们采用了jQuery库,很多兼容性问题jQuery库已经帮我们解决了。

    不提供源代码的控件分析都是耍流氓,单击'这里',下载讲解的示例代码。

    完整的全国'三级地址',小生正在整理中,诸位大哥如果想要一起研究,共同完善,请留下邮箱,待小生整理完毕之后,会即时向诸位汇报。

    感谢诸位捧场。

  • 相关阅读:
    后缀树到后缀自动机
    bzoj 4199 品酒大会
    BZOJ 4310 跳蚤
    BZOJ 4545 DQS的Trie
    BZOJ 3238 差异
    BZOJ 3277 串
    BZOJ 3926 诸神眷顾的幻想乡
    线程与进程
    SparkSql自定义数据源之读取的实现
    spark提交至yarn的的动态资源分配
  • 原文地址:https://www.cnblogs.com/alai88/p/5412169.html
Copyright © 2011-2022 走看看