搜尋

首頁  >  問答  >  主體

javascript - 動態新增的元素,怎麼遍歷它們的功能及內容

#如圖:要實現的功能是,點擊上方添加按鈕,下邊動態添加表格的行,然後每個行裡的內容都是從對象裡遍歷出來的。
然後我就只有第一個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]);  
        };
    }
    };

我覺得可能要在建立表格的行的時候就應該把內容給遍歷進去?可是我不會誒,還有就是我那個功能怎麼辦。
在線等一個大牛

告訴我!萬分感謝!急!

代言代言2713 天前798

全部回覆(5)我來回復

  • typecho

    typecho2017-06-12 09:30:08

    首先,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...

    回覆
    0
  • 某草草

    某草草2017-06-12 09:30:08

    把遍歷資料的程式碼,放在你點選新增行這個方法的裡面,具體協調如何添加一行遍歷一條,這個還是你自己實現,我沒細看。你現在有點象動態加入的元素,你綁定事件,結果再html寫好的綁定上去了,但是添加的沒綁定上事件一樣

    回覆
    0
  • 欧阳克

    欧阳克2017-06-12 09:30:08

    建議直接用 ui 函式庫吧,jquery 的話,可以選 easyui / bootstrap table 吧

    回覆
    0
  • 学习ing

    学习ing2017-06-12 09:30:08

    事件綁定在頁面渲染完成之後就會綁定成功,之後不會自動綁定,也就是頁面渲染完之後添加的節點是無法綁定的,這裡說的是$('.a').click (...) 這種類似的方式因為第一次編譯的時候會把頁面上class為A的綁定一個事件,如果你JS動態條件幾個class為a的節點那麼之後加的是沒有效果的,因為第一次綁定的時候後新增的節點不存在。
    解決方式推薦用on()這個方法例如$('body').on('click','.a',fun(){}) 這種方式就可以,因為不管什麼時候body這個標籤都會存在所以不會出現節點找不到而無法綁定的情況。
    當然 你也可以自己將函數寫在節點上 但是 不推薦這樣寫。

    回覆
    0
  • 高洛峰

    高洛峰2017-06-12 09:30:08

    我改寫了一部分產生 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 也行。

    回覆
    0
  • 取消回覆