JavaScript TypeError: Cannot read property 'id' of undefined

JavaScript TypeError: Cannot read property 'id' of undefined

前言:

我有一个 HTML table,用户可以在其中填写姓名和 URL 他们拥有帐户的社交网络。每行都有一个复选框。在 jQuery 和 JavaScript 的帮助下,按下“添加行”按钮时,一行会动态添加到 table 中,按下“删除行”按钮时,选中复选框的那些行将被删除。

Objective:

删除选定的行后,我想将 id 分配给每个剩余行的输入元素和复选框以串行方式,以便这些行始终是连续的。

问题:

我找不到导致它抛出错误的原因,因为 childNodes[] 似乎不会导致元素不可用。请帮忙!

片段:

    // variable to keep track of rows(records)
    var social_recs_num = 1;
    $(document).ready(function () {
    
      $("#social_add_rec").click(function () {
    
        social_recs_num++;
        var markup = "<tr> <th scope=\"row\"> <div class=\"form-group\"> <div class=\"form-check\"> <input type=\"checkbox\" class=\"form-check-input\" id=\"social_rec" + social_recs_num + "\" name=\"social_rec\"> <label class=\"form-check-label\" for=\"social_rec" + social_recs_num + "\"> " + social_recs_num + "</label> </div></div></th> <td> <div class=\"form-group shadow-sm\"> <label for=\"name_social" + social_recs_num + "\">Select or Type</label> <input type=\"text\" list=\"name_social" + social_recs_num + "\" class=\"form-control\" placeholder=\"Name\"> <datalist id=\"name_social" + social_recs_num + "\"> <option value=\"Wikipedia\">Wikipedia</option> <option value=\"Youtube\">Youtube</option> <option value=\"Facebook\">Facebook</option> <option value=\"Twitter\">Twitter</option> <option value=\"Pinterest\">Pinterest</option> </datalist> </div></td><td> <div class=\"form-group shadow-sm\"> <textarea class=\"form-control\" id=\"url_social" + social_recs_num + "\" cols=\"30\" rows=\"1\" placeholder=\"URL\"></textarea> </div></td></tr>"; //in case of id we need to do it like: "min_sysreq" +social_recs_num...as social_recs_num has been incremented priorly it will be the row number of row to be added
    
        $("table tbody").append(markup);
    
      });
    
      // Find and remove selected table rows...only condition is that there must be present attribute name='sysreq_rec' in input element for checkbox of every row
    
      $("#social_delete_rec").click(function () {
    
        $("table tbody").find('input[name="social_rec"]').each(function () {
    
          if ($(this).is(":checked")) {
            social_recs_num = social_recs_num - $(this).length;
            $(this).parents("tr").remove();
    
          }
    
        });
        // to be run when a row is deleted...this assigns ids to input elements in serial manner            
        var table = document.getElementById("mytab1");
        var rowCount = table.rows.length;
        
        // i has been initiated with 1, as header row is the 0th row
        for (var i = 1; i < rowCount; i++) {
          var row = table.rows[i];
    
          // on this line, getting error: `Uncaught TypeError: Cannot read property 'id' of undefined`
          row.childNodes[0].childNodes[0].childNodes[0].childNodes[0].id = "social_rec" + i;
          row.childNodes[0].childNodes[0].childNodes[0].childNodes[1].for = "social_rec" + i;
          row.childNodes[0].childNodes[0].childNodes[0].childNodes[1].innerHTML = " " + i;
    
          row.childNodes[1].childNodes[0].childNodes[0].for = "name_social" + i;
          row.childNodes[1].childNodes[0].childNodes[1].list = "name_social" + i;
          row.childNodes[1].childNodes[0].childNodes[2].id = "name_social" + i;
    
          row.childNodes[2].childNodes[0].childNodes[0].id = "url_social" + i;
    
          //iterate through rows
          //rows would be accessed using the "row" variable assigned in the for loop
    
        }
    
      });
    
    });
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table class="table table-bordered" id="mytab1">
      <thead>
        <tr>
          <th scope="col">
            #
          </th>
          <th scope="col">
            Name
          </th>
          <th scope="col">
            URL
          </th>
        </tr>
      </thead>
      <tbody>
        <tr>
          <th scope="row">
            <div class="form-group">
              <div class="form-check">
                <input type="checkbox" class="form-check-input" id="social_rec1" name="social_rec">
                <label class="form-check-label" for="social_rec1"> 1</label>
              </div>
            </div>
          </th>
          <td>
            <div class="form-group shadow-sm">
              <label for="name_social1">Select or Type</label>
              <input type="text" list="name_social1" class="form-control" placeholder="Name">
              <datalist id="name_social1">
                <option value="Wikipedia">Wikipedia</option>
                <option value="Youtube">Youtube</option>
                <option value="Facebook">Facebook</option>
                <option value="Twitter">Twitter</option>
                <option value="Pinterest">Pinterest</option>
              </datalist>
            </div>
          </td>
          <td>
            <div class="form-group shadow-sm">
              <textarea class="form-control" id="url_social1" cols="30" rows="1" placeholder="URL"></textarea>
            </div>
          </td>
        </tr>
      </tbody>
    </table>
    <button class="btn btn-primary" type="button" id="social_add_rec">Add Row</button>
    <button class="btn btn-primary" type="button" id="social_delete_rec">Delete Row</button>

P.S。 :

  1. 我用过jQuery和Bootstrap。
  2. 问题似乎很接近,但我想我没有犯同样的错误。

编辑:

我需要显示(或存储)用户的输入,为此我编写了以下 JavaScript 代码。

var social = function () {

    var val = "social:\n";
    for (x = 1; x <= social_recs_num; x++) {
      //where social_recs_num is number of rows
      val = val + "\s\s-\n";
      val = val + "\s\s\s\s" + "name: " + getElementById("name_social" + x) + "\n";
      val = val + "\s\s\s\s" + "url: " + getElementById("url_social" + x) + "\n";
    }

    return val;

}

function social returns YAML格式用户输入的值。对于此代码,当删除选定的行时,我想以串行方式将 id 分配给输入元素和每个剩余行的复选框。

问题:

您遇到的问题是因为您试图获取 html 元素的 'childNodes' 属性。为了访问 属性,您必须通过在其周围添加“$( )”将元素转换为 jQuery 对象。在这种情况下,这将是“$(row)”。

但是,如果您对代码执行此操作,由于嵌套的 .childNodes[0] 混乱,您仍然会出错。您可以使用现有代码使其工作,但是...

建议重构:

...正如其他人所说,有更优雅的方法来解决这个问题。有多种方法可以解决它,但考虑到您当前的 html,我会重构为如下内容:

//...


// to be run when a row is deleted...this assigns ids to input elements in serial manner                
$('#mytab1 tbody tr').each(function() {
    var rowNum = $(this)[0].rowIndex;

    var formcheck = $(this).find('.form-check');  
    formcheck.find('.form-check-input')[0].id = 'social_rec' + rowNum;
    formcheck.find('.form-check-label')[0].for = 'social_rec' + rowNum;
    formcheck.find('.form-check-label')[0].innerHTML = ' ' + rowNum;

    var formName = $(this).find('td')[0];
    $(formName).find('label')[0].for = 'social_rec' + rowNum;
    $(formName).find('input')[0].list = 'social_rec' + rowNum;
    $(formName).find('datalist')[0].id = 'social_rec' + rowNum;

    var formUrl = $(this).find('td')[1];
    $(formUrl).find('textarea')[0].id = 'social_rec' + rowNum;          
});


//...

这会用 jquery.each 循环替换您的 for 循环,然后使用 .find() 函数来循环您要操作的每个控件。您还会注意到它正在使用 rowIndex() 函数来确定分配给行的编号。

我认为使用这种方法的优势在于它更容易阅读代码并确定受影响的控件。您还可以另外重构 html 以使用您想要定位的控件独有的 classes,然后您可以只使用 class 选择器来更改这些控件。

希望对您有所帮助!