【问题标题】:JavaScript TypeError: Cannot read property 'id' of undefinedJavaScript TypeError:无法读取未定义的属性“id”
【发布时间】:2019-08-11 14:31:33
【问题描述】:

前言:

我有一个 HTML 表格,用户在其中填写他们拥有帐户的社交网络的名称和 URL。每行都有一个复选框。在 jQuery 和 JavaScript 的帮助下,按下 Add Row 按钮时,会动态地将一行添加到表中,按下 delete row 按钮时,那些选中复选框的行将被删除。

目标:

当删除选定的行时,我想以串行方式分配 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>

附言:

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

编辑:

我需要按用户显示(或存储)输入,为此我编写了以下 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 以 YAML 格式返回用户输入的值。对于此代码,当删除选定的行时,我想以串行方式将id 分配给剩余每一行的输入元素和复选框。

【问题讨论】:

  • 在我看来,如果您在此之前由于某种原因不需要该索引,那么当您要提交/保存它们时序列化/索引它们要比在界面中动态地容易得多。跨度>
  • 我想以串行方式将 id 分配给剩余每一行的输入元素和复选框,以便这些行总是连续正确的。 这不是真的,会导致一个非常脆弱的解决方案,需要大量维护并且无法很好地扩展。您始终可以通过 id 以外的其他方式识别元素。
  • 有比链接childNodes[0].childNodes[0].childNodes[0]...更简洁的方法来定位嵌套元素。仅仅阅读它就不可能知道最后是什么。混合原生方法和 jQuery 方法也不是一个好习惯。使用其中一种并保持一致
  • @NathanChampion 谢谢。我实际上需要为另一个功能连续分配 id。我已经相应地编辑了这个问题,并提供了进一步的解释。
  • @ScottMarcus 我对 JS 很陌生。我是否应该知道一些比 ids 识别元素更好的替代方法(也许对于这种情况)?问题中的代码是我经过一番谷歌搜索和努力后得出的,但我很想知道是否有更可扩展的替代方案。我也进一步澄清了这个问题。

标签: javascript jquery html delete-row typeerror


【解决方案1】:

问题:

您看到的问题是因为您试图获取 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 以使用您想要定位的控件所独有的类,然后您可以只使用类选择器来更改这些控件。

希望有帮助!

【讨论】:

  • 谢谢。这很有帮助。
  • 虽然代码没有给出错误,但它似乎没有按应有的方式运行。在添加了 4 行之后,如果我们删除第 3 行,则datalist 标签对剩余 3 行中的第 3 行无效。似乎代码无法分别更改labelinput 标签的forlist 属性。旁注:在formNameformUrl 的情况下,name_socialurl_social 应分别替换代码中的social_rec。这是Codepen
猜你喜欢
  • 1970-01-01
  • 2017-06-29
  • 2020-03-06
  • 2021-09-06
  • 2018-12-02
  • 2021-05-29
  • 1970-01-01
  • 1970-01-01
  • 2017-01-24
相关资源
最近更新 更多