为了账号安全,请及时绑定邮箱和手机立即绑定

JS处理Select Option 过滤

标签:
JavaScript

今天在开发中遇到一个开发需求要求在为产品添加SKU属性时,已经选择过的属性在后面的Select中将不能再被选择。视图如下图:
图片描述

一、根据这个视图分为三个操作:

 1. 属性选择框选择属性操作 ----- change.selet
 2. 新增属性操作 ----- click.add
 3. 删除属性操作 ----- click.del

二、下面根据这三个操作关联相应的DOM更新:

 1. change.selet:当属性选择操作发生change事件时,
                  存储下当前的选择值 selected_value。
                  遍历文档中存在的Select元素并将该元素下,
                  值等于selected_value的Option选项的disabled属性设置为true;
     $(document).on('change.select','.js-select-property',function(){
        var _this = $(this),
            _selectedValue = _this.val(),
            _root = _this.parents('._specification-group'),
            _row = _this.parents('._property_item'),
            _siblings = _row.siblings('._property_item'),
            _rows = _root.find('._property_item'),
            _original = _this.data('original');

        _siblings.each(function(index,item){
            var _item = $(this).find('option[value='+_selectedValue+']'),
                _originalIitem = $(this).find('option[value='+_original+']');
            _item.prop("disabled", true);
            _originalIitem.prop("disabled", false);
        })

        _this.data('original',_selectedValue)
    })

 2. click.add: 当点击新增按钮时,
                遍历文档中存在的Select元素并获取该元素的value值,
                新建一个Option元素值为当前获取的值,disabled属性为false。
                并插入一个定义的new_select 元素中,遍历结束,
                将定义的new_select 元素插入到文档中;
   $(document).on('click.add','.js-add-new-property',function(){
        var _this = $(this),
            _root = _this.parents('._specification-group'),
            _rows = _root.find('._property_item'),
            _row = _this.parents('._property_item'),
            _length = _rows.length,         
            _newItem = '<div class="row _property_item"> '
                + ' <label class="control-label _ml20 _inline_block">属性名称</label>'
                + ' <select data-original="0" class="form-control _inline_block _w240 _ml20 js-select-property" name="">'
                + '<option value="0">请选择规格属性名称</option>'
                + '<option value="1">颜色</option>'
                + '<option value="2">尺寸</option>'
                + '<option value="3">材质</option>'
                + '</select>'
                + ' <div class="input-group _w320 _vam _inline_block _ml20">'
                + '<input type="text" class="form-control _w240">'
                + '<span class="input-group-btn">'
                + '<button type="button" class="btn btn-danger js-del-new-property">删除</button>'
                + '</span></div></div>',
            _newItemDom = $(_newItem);
                if(_length === 3){
                layer.msg('最多只能添加三条');
                    return
               }
            _rows.each(function(item){              
                var val = $(this).find('select').val(),
                    _item = _newItemDom.find('option[value='+val+']');  
                    _item.prop("disabled", true);
            });
            _row.after(_newItemDom);        
       })
 3. click.del:当点击删除按钮时,
               存储下当前的选择值 selected_value。
               遍历文档中存在的Select元素并将该元素下,
               值等于selected_value的Option选项的disabled属性设置为false;
    $(document).on('click.del','.js-del-new-property',function(){
        var _this = $(this),
            _root = _this.parents('._specification-group'),
            _rows = _root.find('._property_item'),
            _row = _this.parents('._property_item'),
            _delValue = _row.find('select').val(),
            _siblings = _row.siblings('._property_item');
        _rows.each(function(index,item){
            var _item = $(this).find('option[value='+_delValue+']');
            _item.prop('disabled',false)
        });
        _row.remove()
    })

三、总结:
1、 在选择属性时绑定的Change.select事件会将兄弟Select元素中等值的Option的Disabled属性设置为True,但是如果再次更新该Select元素值时,该Change.select事件只会重复的将兄弟Select元素中等值的Option的Disabled属性设置为True,这样就会造成兄弟元素中的Option元素都被设置成不可选状态,显然这是一个Bug;解决这个问题的办法就是在Change.select事件发生时对原来值对应的兄弟元素中的Option进行状态的释放,这里就遇到一个问题如何获得Select元素的上个状态的值,查了下Select元素的API发现并没有记录上个状态的属性,于是就设置了一个自定义属性data-original用来手动记录Select的上一个状态值;并利用_original = _this.data('original')获取,_this.data('original',_selectedValue)进行更新;

四、功能完善:

前端写好静态HTML页面往往需要在和后端对接的时候对页面进行调整,做一些完善功能、调整布局的操作;在这个案例中和后端对接遇到两个问题:

 1. form表单元素要动态的插入name属性,这个name属性是个数组并且在插入式填写数组的下标;
 2. 为了便于后端渲染,添加按钮要从第一条条目中独立出来;

下面是修改后的HTML结构:

<!-- 产品规格 -->
<div class="form-group">
    <label class="col-sm-2 control-label">产品规格
    </label>
    <div class="col-sm-10 js-specification-groups">
        <div class="blank"></div>
        {volist name='data.sku_item' id='vo'}
        <div class="_specification-group">
            {if condition="$vo.sku_color"}
            <div class="row _property_item">
                <label class="control-label _ml20 _inline_block">属性名称</label>
                <select data-original="0" class="form-control _inline_block _w240 _ml20 js-select-property" name="attr[1][]">
                    <option value="0">请选择规格属性名称</option>
                    <option value="color" selected>颜色</option>
                    <option value="size">尺寸</option>
                    <option value="fabric">材质</option>
                </select>
                <div class="input-group _w320 _vam _inline_block _ml20">
                    <input type="text" name="attr_cont[1][]" class="form-control _w240 js-input-property" value="{$vo.sku_color}">
                    <span class="input-group-btn">
                                    <button type="button" class="btn btn-danger js-del-new-property">删除</button>
                                </span>
                </div>
            </div>
            {/if} {if condition="$vo.sku_size"}
            <div class="row _property_item">
                <label class="control-label _ml20 _inline_block">属性名称</label>
                <select data-original="0" class="form-control _inline_block _w240 _ml20 js-select-property" name="attr[1][]">
                    <option value="0">请选择规格属性名称</option>
                    <option value="color">颜色</option>
                    <option value="size" selected>尺寸</option>
                    <option value="fabric">材质</option>
                </select>
                <div class="input-group _w320 _vam _inline_block _ml20">
                    <input type="text" name="attr_cont[1][]" class="form-control _w240 js-input-property" value="{$vo.sku_size}">
                    <span class="input-group-btn">
                        <button type="button" class="btn btn-danger js-del-new-property">删除</button>
                    </span>
                </div>
            </div>
            {/if} {if condition="$vo.sku_fabric"}
            <div class="row _property_item">
                <label class="control-label _ml20 _inline_block">属性名称</label>
                <select data-original="0" class="form-control _inline_block _w240 _ml20 js-select-property" name="attr[1][]">
                    <option value="0">请选择规格属性名称</option>
                    <option value="color">颜色</option>
                    <option value="size">尺寸</option>
                    <option value="fabric" selected>材质</option>
                </select>
                <div class="input-group _w320 _vam _inline_block _ml20">
                    <input type="text" name="attr_cont[1][]" class="form-control _w240 js-input-property" value="{$vo.sku_fabric}">
                    <span class="input-group-btn">
                        <button type="button" class="btn btn-danger js-del-new-property">删除</button>
                    </span>
                </div>
            </div>
            {/if}
            <div class="row _mt10 js-add-new-property-box">
                <div class="control-label _ml20 _inline_block _w52 _txt_align_just"></div>
                <div class="_inline_block _w555 _ml20">
                    <button type="button" class="btn btn-primary js-add-new-property">
                        新增产品属性
                    </button>
                </div>
            </div>
            <div class="row _mt10">
                <label class="control-label _ml20 _inline_block _w52 _txt_align_just">库存</label>
                <input type="text" class="form-control _inline_block _w555 _ml20 js-stock" name="stock[1]" value="{$vo.stock}">
            </div>
        </div>
        {/volist}
        <div class="row _mt10">
            <div class="control-label _ml20 _inline_block _w52 _txt_align_just"></div>
            <div class="_inline_block _w555 _ml20">
                <button class="btn btn-primary js-add-new-specification" type="button">新增产品规格</button>
            </div>
        </div>
    </div>
</div>
<div class="hr-line-dashed"></div>

处理好这些调整之后就是一些对JS代码的一些优化,这里我做了一些调整:

1.利用JQuery的clone方法获取Dom转化成模版的方法var _newItemDom = $(this).parents('.js-add-new-property-box').prev('._property_item').clone();取代以前写长长的模版字符串的方法_newItem = '<div class="row _property_item">....</div>',

2.利用JQuery设置属性方法 _new_specification_dom.attr('name','attr['+ n +'][]') 取代拼接字符串设置属性的方法name=attr_cont[' + n + '][]

3.利用JQuery隐式迭代批量快速操作对象属性;

4.利用JQuery链式操作快速进行文档树遍历;

5.使用JQuery:get(index)获取操作对象的区间;

6.使用.match(正则),获取字符串中指定位置的字符串;

7.对插入新属性组进行排序性插入,利用序号和标号对比确定是否顺序以及插入的位置;

优化后的JS代码如下:

// 添加属性     
$(document).on('click.add','.js-add-new-property',function(){
    var _this = $(this),
        _root = _this.parents('._specification-group'),
        _rows = _root.find('._property_item'),
        _wrap = _this.parents('.js-add-new-property-box'),
        _row = _wrap.prev('._property_item'),
        _length = _rows.length,             
        _newItemDom = _row.clone();
    if(_length === 3){
        layer.msg('最多只能添加三条');
        return
    }
    _newItemDom             
        .find('.js-input-property')
        .val('')
        .end()
        .find('.js-select-property')
        .val(0)
        .find('option')
        .removeAttr('selected');
    _rows.each(function(item){              
        var val = $(this).find('select').val(),
            _item = _newItemDom.find('option[value='+val+']');  
        _item.prop("disabled", true);
    });
    _wrap.before(_newItemDom);      
})
// 删除属性
$(document).on('click.del','.js-del-new-property',function(){
    var _this = $(this),
        _root = _this.parents('._specification-group'),
        _rows = _root.find('._property_item'),
        _length = _rows.length,
        _row = _this.parents('._property_item'),
        _delValue = _row.find('select').val(),
        _siblings = _row.siblings('._property_item');
    if(_length === 1){
        layer.msg('至少有一条属性');
        return
    }
    _rows.each(function(index,item){
        var _item = $(this).find('option[value='+_delValue+']');
        _item.prop('disabled',false)
    });
    _row.remove()
})
// 选择属性
$(document).on('change.select','.js-select-property',function(){
    var _this = $(this),
        _selectedValue = _this.val(),
        _root = _this.parents('._specification-group'),
        _row = _this.parents('._property_item'),
        _siblings = _row.siblings('._property_item'),
        _rows = _root.find('._property_item'),
        _original = _this.data('original');

    _siblings.each(function(index,item){
        var _item = $(this).find('option[value='+_selectedValue+']'),
            _originalIitem = $(this).find('option[value='+_original+']');
        _item.prop("disabled", true);
        _originalIitem.prop("disabled", false);
    })

    _this.data('original',_selectedValue)
})
// 添加属性组
$(document).on('click.addGroup','.js-add-new-specification',function(){
    var _this = $(this),
        _root = _this.parents('.js-specification-groups'),
        _groups = _root.find('._specification-group'),
        _length = _groups.length,
        n = _length + 1,
        _insert = false,
        _insert_position = 0,
        _add_btn = _this.parents('.row._mt10'),             
        _new_specification_dom = _groups.eq(0).clone();

    _new_specification_dom
        .addClass('_mt10')
        .prepend($('<div class="hr-line-dashed _w635"></div>'))
        .find('input')
        .val('')
        .end()
        .find('option')
        .prop("disabled", false)
        .end()
        .find('.js-stock')
        .removeClass('_w555')
        .addClass('_w460')
        .after($('<span class="input-group-btn _inline_block"><button type="button" class="btn btn-danger js-del-spec">删除该规格</button></span>'))
        .end()
        .find('.js-del-new-property')
        .parents('._property_item:gt(0)')               
        .remove();

    _groups.each(function(index,item){
        var _this = $(this),
            _select = _this.find('select'),
            _name = _select[0].name,
            _index = index+1,
            i = _name.match(/attr\[(\d*)\]/),
            _i = Number(i[1]);
        if(_index !== _i){                  
            n = _index,
            _insert_position = index,
            _insert = true;             
            return false;
        }else{
            _insert = false
        }
    }); 

    _new_specification_dom
        .find('.js-select-property')
        .attr('name','attr['+ n +'][]')
        .end()
        .find('.js-input-property')
        .attr('name','attr_cont['+ n +'][]')
        .end()
        .find('.js-stock')
        .attr('name','stock['+ n +']'); 

    _insert ? _groups.eq(_insert_position)
                     .before(_new_specification_dom)
            : _add_btn.before(_new_specification_dom);
});
// 删除属性组
$(document).on('click.delGroup','.js-del-spec',function(){
    $(this).parents('._specification-group').remove();
});

调整后:
图片描述

点击查看更多内容
2人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消