javascript - 动态添加的元素,怎么遍历它们的功能及内容
代言
代言 2017-06-12 09:28:08
0
5
818

如图:要实现的功能是,点击上方添加按钮,下边动态添加表格的行,然后每个行里的内容都是从对象里遍历出来的。
然后我就只有第一个html里写出来的行有内容有功能。
而之后点击按钮出来的行,没有内容也没有功能。(注:功能指的是,我的第一个列和第二个列,是二级联动效果的)

下面是代码:(本人菜鸟一个,jq也不是很熟,就写的很乱,而且还是js和jq混着写的=-=,求大佬们轻喷,求指导!)

    <p class="clearfix" style="margin-left:200px;">
    <button class="button2" style="border:1px solid #000">添加</button>
    <form class="clearfix" style="float:left;margin-top:100px;">
        <table id="b" border="1px solid #000">
            <thead>
                <tr class="zzz">
                    <th style="width:141px;">计算期类型</th>
                    <th style="width:281px;">期间</th>
                    <th style="width:141px;">征收方式</th>
                </tr>
            </thead>
            <tbody id="zType_all">
                <tr>
                    <td>
                        <select id="zType_time"></select>
                    </td>
                    <td>
                        <select id="zType_years" style="float:left;"></select>
                        <select id="month_quarter"></select>
                    </td>
                    <td>
                        <select id="zCollection">
                            <option value="chazhang">查账征收</option>
                            <option value="heding">核定征收</option>
                        </select>
                    </td>
                </tr>
            </tbody>
        </table>
    </form>
    </p>
//点击按钮添加

    $('.button2').on('click',function(){
    var ccc = $('<tr><td><select id="zType_time"></select></td><td><select id="zType_years"></select><select id="month_quarter"></select></td><td><select id="zCollection"><option value="chazhang">查账征收</option><option value="heding">核定征收</option></select></td></tr>');
    var ddd = $('#zType_all');
    ddd.append(ccc);
    $('#a').css({"background":"white","color":"#000"});
    });



//下面是关于二级和三级联动的遍历
//这个是存储着option里信息的对象

    var zType_chose = [
    {
    "name":"年终汇算","types":[
        {"years":"2015年"},
        {"years":"2016年"}
        ]
     },
     {
        "name":"预缴-月度","types":[
        {"years":"2015年","zType_time1":["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"]},
        {"years":"2016年","zType_time1":["1月","2月","3月","4月","5月","6月","7月","8月","9月","10月","11月","12月"]}
        ]
     },
     {
        "name":"预缴-季度","types":[
        {"years":"2015年","zType_time1":["第一季度","第二季度","第三季度","第四季度"]},
        {"years":"2016年","zType_time1":["第一季度","第二季度","第三季度","第四季度"]}
        ]
     }
     ];

    //下面是我实现联动的js代码
    var zType_time = document.getElementById("zType_time");
    var zType_years = document.getElementById("zType_years");
    var month_quarter = document.getElementById("month_quarter");
    var zType_all = document.getElementById("zType_all");
    zType_time.options[0] = new Option("计算期类型");
    zType_years.options[0] = new Option("请选择年");
    month_quarter.options[0] = new Option("请选择月/季度");
    // 循环第一步把计算期类型循环进select
    for (var i = 0; i < zType_chose.length; i++) {
    zType_time.options[zType_time.length] = new Option(zType_chose[i].name);
    // 循环第二步,把第二列都循环进select
    zType_time.onchange = function(){
    zType_years.options.length = 0;
    month_quarter.options.length = 0;
    zType_years.options[zType_years.length] = new Option("请选择年");
    month_quarter.options[month_quarter.length] = new Option("请选择月/季度");
    for (var j = 0; j < zType_chose[zType_time.selectedIndex-1].types.length; j++) {
        zType_years.options[zType_years.length] = new Option(zType_chose[zType_time.selectedIndex-1].types[j].years)
    }
    if(zType_time.options[zType_time.selectedIndex].text == "年终汇算"){
        month_quarter.style.display = "none";
    }else{
        month_quarter.style.display = "inline-block";
    }
    }
    zType_years.onchange = function(){
        month_quarter.options.length = 0;
        month_quarter.options[month_quarter.length] = new Option("请选择月/季度");
        //循环另一个
        for (var k = 0; k < zType_chose[zType_time.selectedIndex-1].types[zType_years.selectedIndex-1].zType_time1.length; k++) {
            month_quarter.options[month_quarter.length] = new Option(zType_chose[zType_time.selectedIndex-1].types[zType_years.selectedIndex-1].zType_time1[k]);  
        };
    }
    };

我觉得可能是要在创建表格的行的时候就应该把内容给遍历进去?可是我不会诶,还有就是我那个功能怎么办。
在线等一个大牛

告诉我!万分感谢!急!

代言
代言

全部回复(5)
typecho

首先,id 在一个页面中只能出现一次,是唯一的,class才可以多次出现!

声明

id 为唯一属性,即一个 html 文件中只能出现一个对应的 idclass 可以使用多个;

需求实现

前端部分大到框架,小到功能,都是可以用设计模式来解决的,说白了,都是可以用 MVC 来解决的,如你的的例子:

  • C -> 整个 JS 逻辑;

  • V -> 单行条目;

  • M -> 填充的数据;

你其实可以把这种东西进行闭包封装,或者做成一个处理类。如:

<p class="clearfix" style="margin-left:200px;">
    <button class="button2" style="border:1px solid #000">添加</button>
    <form class="clearfix" style="float:left;margin-top:100px;">
        <table border="1">
            <thead>
                <tr class="zzz">
                    <th width="240">计算期类型</th>
                    <th width="240">期间</th>
                    <th width="128">征收方式</th>
                </tr>
            </thead>
            <tbody id="zType_all">
            </tbody>
        </table>
    </form>
</p>

继续完善脚本:

(function($) {
    'use strict';

    /**
     * 组件条目解析
     * 一行即一个组件条目,该构造方法
     * 例:var thisRow = new AppRow();
     * @return {object} 一个 jQuery 对象
     */
    var AppRow = function() {
        return this.rowParse(this.rowDom());
    };

    // HTML 结构
    AppRow.prototype.rowDom = function() {
        var row = '',
            options = AppRow.typeChose,
            optionYears = options[0].types;

        // 计算期类型
        row += '<td><select class="type">';
        row += '<option value="-1">请选择</option>';
        for (var i in options) {
            row += '<option value="' + i + '">' + options[i].name + '</option>';
        }
        row += '</select></td>';

        // 期间
        row += '<td class="types"></td>';

        // 征收方式
        row += '<td><select class="collection">';
        row += '<option value="0">请选择</option>';
        row += '<option value="1">查账征收</option>';
        row += '<option value="2">核定征收</option>';
        row += '</select></td>';

        return '<tr>' + row + '</tr>';
    };

    // 解析行结构,添加事件
    AppRow.prototype.rowParse = function(row) {
        var $row = $(row),
            $typeTime = $row.find('select.type'),
            $typeTypes = $row.find('.types'),
            optionsData = AppRow.typeChose;

        // 计算期类型
        $typeTime.on('change.app', $.proxy(function(e) {
            var val = $typeTime[0].value;
            $typeTypes.children().remove();
            $typeTypes.append(this.selectYears(val));
        }, this));

        return $row;
    };

    // 返回年度表单
    AppRow.prototype.selectYears = function(type) {
        var optionYears = AppRow.typeChose[0].types,
            select = '',
            $select = null;

        // 构建表单
        select += '<select class="year">';
        select += '<option value="-1">请选择</option>';
        for (var i in optionYears) {
            select += '<option value="' + i + '">' + optionYears[i].years + '</option>';
        }
        select += '</select>';

        // 构建成 jQuery 对象
        $select = $(select);

        // 事件绑定
        $select.on({
            // 选择事件
            'change.app': $.proxy(function(e) {
                var val = $select[0].value,
                    $sub = $select.next('.sub');

                if (type <= 0) {
                    // 年终汇算无后续表单
                    return;
                }

                if (val === -1) {
                    // 未选择,则移除后续表单
                    $select.nextAll().remove();
                    return;
                }

                // 创建或或初始化后续表单
                if (!$sub.get(0)) {
                    // 表单不存在则创建
                    $select.after(this.selectSub(type, val));
                }
                $sub.trigger('reset.app');
            }, this),

            // 重置表单
            'reset.app': $.proxy(function(e) {
                $select.find('option').prop('selected', false)
                    .eq(0)
                    .prop('selected', true);
            })
        });

        return $select;
    };

    // 返回季度或月份表单
    AppRow.prototype.selectSub = function(type, year) {
        var optionData = AppRow.typeChose[type].types[year].zType_time1,
            select = '',
            $select = null;

        if (!optionData) {
            // 无数据
            return;
        }

        // 构建表单
        select += '<select class="sub">';
        select += '<option value="-1">请选择</option>';
        for (var i in optionData) {
            select += '<option value="' + i + '">' + optionData[i] + '</option>';
        }
        select += '</select>';
        $select = $(select);

        // 事件绑定
        $select.on({
            // 重置表单
            'reset.app': $.proxy(function(e) {
                $select.find('option').prop('selected', false)
                    .eq(0)
                    .prop('selected', true);
            })
        });

        return $select;
    };

    // 联动数据 - zType_chose
    AppRow.typeChose = [{
        "name": "年终汇算",
        "types": [{
            "years": "2015年"
        }, {
            "years": "2016年"
        }]
    }, {
        "name": "预缴-月度",
        "types": [{
            "years": "2015年",
            "zType_time1": ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"]
        }, {
            "years": "2016年",
            "zType_time1": ["1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"]
        }]
    }, {
        "name": "预缴-季度",
        "types": [{
            "years": "2015年",
            "zType_time1": ["第一季度", "第二季度", "第三季度", "第四季度"]
        }, {
            "years": "2016年",
            "zType_time1": ["第一季度", "第二季度", "第三季度", "第四季度"]
        }]
    }];

    AppRow.VERSION = '1.0.0';

    // ===========================================

    // 添加按钮事件绑定
    var $addBtn = $('.button2'),
        $rowList = $('#zType_all');

    // 点击添加
    $addBtn.on('click.app', function(e) {
        // 或直接添加实例
        $rowList.append(new AppRow());
    });

})(jQuery);

解释

  • 创建一个构造方法,用于生成一个具有交互封装效果的结构 —— 组件;

  • 为组件添加一系列方法,并在内部创建逻辑;

  • 闭包封装指仅 (function($){ /*code*/ })(jQuery); 中生效;

  • $.fn.remove可以保证页面的数据的安全性,切忌使用 $.fn.hide

  • 还可以生使用 AppRow.prototype.delete() 扩展删除操作等;

  • 如果需要使用 name 做数据提交,则需使用 name="row[index][name]" 的格式,如:name="row[1]['type']"name="row[1]['year']"name="row[1]['sub']";

最终效果:

在线效果

https://jsfiddle.net/kxjyo7fp...

某草草

把遍历数据的代码,放在你点击添加行这个方法的里面,具体协调如何添加一行遍历一条,这个还是你自己实现,我没细看。你现在有点象动态添加的元素,你绑定事件,结果再html写好的绑定上去了,但是添加的没绑定上事件一样

学霸

建议直接用 ui 库吧,jquery 的话,可以选 easyui / bootstrap table 吧

学习ing

事件绑定在页面渲染完成之后就会绑定成功,之后不会自动绑定,也就是页面渲染完之后添加的节点是无法绑定的,这里说的是$('.a').click(...) 这种类似的方式 因为第一次编译的时候 会把页面上class为A的绑定一个事件,如果你JS动态条件几个class为a的节点 那么之后加的是没有效果的,因为第一次绑定的时候后添加的节点不存在。
解决方式推荐用on()这个方法 例如$('body').on('click','.a',fun(){}) 这种方式 就可以,因为不管什么时候 body这个标签都会存在 所以不会出现节点找不到而无法绑定的情况。
当然 你也可以自己将函数写在节点上 但是 不推荐这样写。

小葫芦

我改写了一部分生成 DOM 的脚本,用 jQuery 的方式,为了快速,用了一些 ES6 的语法。但尚未完成功能

https://jsfiddle.net/jrhju549/1/

然后在考虑级联这部分东西的时候,我发现了一个问题(关于数据):

从数据上来看,不管是哪年月缴和季度的可选数据都是一样的,月是12个月,季是4季度,那么这些数据其实都是固定的,可以直接写在 HTML 中,这样产生新行的时候只需要先克隆再进行细节处理就行了,这样简单得多。

但是,不排除另一种可能性,就是你每年的可选项都不一样。比如 2015 是 12 个月,2016 是 8 个月……(当然这种情况的可能性很低),那这样的话,就可根据这个数据来动态生成 select。那么在动态生成 select 之前,需要通过一个 findXxxx 函数来找到对应的 option 列表需要使用的数据——这个过程当然就会复杂得多。

就目前的进展来看,主要有几点说明一下

模块化处理

不要想在一个函数中干所有事情,拆分一下,比如我在代码中初步拆分了 createRow 和 createSelect。其中 createSelect 是可以高度复用的

// 创建 select,items 是数据 map 是名称映射表
// 如果名称本来就是按 value 和 text 来命名,就不需要映射表
function createSelect(items, map = {}) {
    const value = map.value || "name";
    const text = map.text || "text";
    const $options = items
        .map(t => $("<option>").prop({ value: t[value] }).text(t[text] || ""));
    return $("<select>").append(...$options);
}
// 创建一个 tr,含 td 和初始化的 select
function createRow() {
    var $tr = $("<tr>");
    var $tds = Array.from(Array(3))
        .map(() => $("<td>"));

    createSelect(types).appendTo($tds[0]);

    createSelect([
        { value: "chazhang", text: "查账征收" },
        { value: "heding", text: "核定征收" }
    ]).appendTo($tds[2]);

    return $tr.append(...$tds);
}

先整理数据

因为 DOM 结构是根据数据来生成的,所以数据就很重要,直观的做法是一边找数据一边进行,但这样看起来代码比较乱。所以一般建议先把对应的数据整理准备好,再根据整理好的数据来生成 DOM

比如 types,就是从 zType_chose 里先抽取出来的

// 生成类型数据
var types = [{ text: "计算机期类型" }]
    .concat(zType_chose.map(t => ({
        value: t.name,
        text: t.name
    })));

整理列表数据的时候,map、filter 和 reduce 基本上是标配(更复杂的情况可以考虑 RxJs)

你已经把 id 改成 class 了,这很好,为后面使用 $.fn.close() 扫清了障碍。因为我没有太多时间进一步去处理,所以把问题留给你:考虑在 HTML 里定义 select,然后在代码里进行细节处理(比如隐藏)。如果 select 还是要根据数据来定义,可以一开始就把第一条的 select 全部填完,方便后面 clone —— 当然这种情况下,把完善 tr 的过程封装在一个函数里,甚至可以不用 clone,直接创建 tr 也行。

热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板