【问题标题】:Iterate through selective cells in a table row JavaScript / jQuery遍历表格行中的选择性单元格 JavaScript / jQuery
【发布时间】:2018-10-29 01:54:21
【问题描述】:

我一直遇到这个问题,我无法通过其他给定的解决方案来解决。

我正在尝试获取表格中每一行的某些值。 问题来自一个相当未格式化的表格,所以我必须根据它们的索引或位置获取单元格值。

这是有问题的表格:

<table width="100%">
    <thead>
        <tr>
            <td>Item Description</td>
            <td>Colour</td>
            <td>Size</td>
            <td class="text-right">Price</td>
            <td class="text-right">Qty</td>
            <td class="text-right">Total</td>
        </tr>
    </thead>
    <tbody>
        <tr id="row_24551">
            <td width="40%">Item 1</td>
            <td>Ivory</td>
            <td>10</td>
            <td class="text-right">$ 19.00</td>
            <td class="text-right">1</td>
            <td class="text-right">$ 19.00</td>
        </tr>
        <tr id="row_24550">
            <td width="40%">Item 2</td>
            <td>Grey Marle</td>
            <td>10</td>
            <td class="text-right">$ 18.95</td>
            <td class="text-right">1</td>
            <td class="text-right">$ 18.95</td>
        </tr>
    </tbody>
</table>

我一直在获取行值

var table = $('tr[id^="row"]') 因为所需的信息在带有 ID 的行中。

可以用

输出每个单元格的内容
var table = $('tr[id^="row"]');
table.each(function(){
    console.log('row');
    $.each(this.cells, function(){
        console.log($(this).text());
    });
});

我卡住的地方是我只想要单元格 [0,2,4,5] 并且我不确定如何通过 .each 函数进行选择。

在有选择性时,For 循环对我来说更有意义,但我无法通过行获得单元格的迭代:

var table = $('tr[id^="row"]');
for(i = 0; i < table.length; i++) { 
    console.log("row");
}

这个也试过了

var table = $('tr[id^="row"]');
for(var i = 0, row; row = table[i]; i++) { 
    console.log("row");
    for (var j = 0, col; col = row[j]; j++) {
        console.log("cell");
    }  
}

但单元格值似乎没有通过。

最终希望将所需的每个单元格值 ( [0,2,4,5] ) 推送到将映射 {name, size, quantity, price} 的产品数组中。但非常感谢您对细胞的帮助!

【问题讨论】:

    标签: javascript jquery


    【解决方案1】:

    你可以像这样使用单元格的索引:

    var table = $('tr[id^="row"]')
    var tabCells = {0: "name", 2: "size", 4: "quantity", 5: "price"} //These are the names of the needed cells
    var tabRows = []; //This will store the rows data
    table.each(function(){
        var tabRow = {}; //This will store the current row data
        $.each(this.cells, function(index){
          if($.inArray(index, [0,2,4,5]) > -1)
            tabRow[tabCells[index]] = $(this).text();
            //e.g: tabRow[tabCells[0]] => tabRow["name"]
        })
        tabRows.push(tabRow);
    })
    

    【讨论】:

    • 这似乎真的很接近我的需要,有没有办法将每个单元格(从索引)值推入一个新数组?我尝试在console.log('row') 所在的位置添加products.push(,但它最终会推入每个td 而不是文本值。
    • 如果我做这样的事情,它也更接近var table = $('tr[id^="row"]'); var products = []; table.each(function(){ $.each(this.cells, function(index){ if($.inArray(index, [0,2,4,5]) &gt; -1) products.push( $(this).text() ); }) }) 但是一个数组中有 2 个产品的信息,而我真正想要的是这个输出:// [{name: "Item 1", size: "10", quantity: "1", price: "$ 19.00"}, // {name: "Item 2", size: "10", quantity: "1", price: "$ 18.95"}] 那会是可能吗?
    • 很明显,虽然我经常浏览 StackOverflow,但我不知道如何正确格式化 cmets。对不起!
    • 谢谢@Hamza,效果很好,我不得不对几个单元格中的一些最终值进行更多编辑,但让它工作了!这对我以后有很大的帮助!
    【解决方案2】:

    试试 cellIndex:

    var table = $('tr[id^="row"]');
    table.each(function() {
        $.each(this.cells, function() {
            if ([0, 2, 4, 5].indexOf(this.cellIndex) > -1) {
                console.log($(this).text());
            }
        });
    });
    

    【讨论】:

      【解决方案3】:

      利用纯 JavaScript,虽然这确实需要相对最新的浏览器,但我想建议以下方法:

      function collateDataByIndices(opts) {
        // settings, used to define some defaults for the function,
        // passing in the opts Object allows those defaults to be
        // overridden.
      
      
        // 'table':              DOM Node, HTMLTableElement,
        // 'indices :            Array of numbers, the indices of which
        //                       cells from which you wish to obtain data,
        // 'rowIndexStartAt :    Number, the rowIndex at which you
        //                       wish to start collecting data.
        // 'tableHeadRowIndex' : the row index of the column
        //                       headings,
        // 'namedByTable':       Uses the table's column headings
        //                       to identify the data in the returned
        //                       Array of Objects,
        // 'keys':               Array of Strings, if you wish to use
        //                       different keys to identify the data
        //                       in the returned Array of Objects.
        let settings = {
          'table': document.querySelector('table'),
          'indices': null,
          'rowIndexStartAt': 1,
          'tableHeadRowIndex': 0,
          'namedByTable': true,
          'keys': false
        };
      
        // here we use the Object.keys() method to retrieve an array
        // of the keys from the supplied 'opts' Object (or an empty
        // Object, to avoid errors being thrown if 'opts' is not supplied):
        Object.keys(opts || {}).forEach(
      
          // we then use Array.prototype.forEach() to iterate over each
          // key of the opts Object (if supplied), and update the
          // settings Object so that supplied options override the
          // defaults:
          key => settings[key] = opts[key]
        );
      
        // if we have no indices supplied, or the length of the supplied
        // Array of indices is 0 (although '!settings.indices.length'
        // would also work, as 0 is a falsey value):
        if (!settings.indices || settings.indices.length === 0) {
      
          // we retrieve the cells of the HTMLTableElement's first
          // row, and use Array.from() to convert that HTMLCollection
          // into an Array (note this is a naive check, since cells
          // that use a `colspan` attribute may cause inaccurate cell
          // counts ('columns') to be retrieved):
          settings.indices = Array.from(
            settings.table.rows[0].cells
      
          // we then use Array.prototype.map(), along with an Arrow
          // function:
          ).map(
      
            // here we pass both 'cell' (the current cell of the
            // Array of cells over which we're iterating) and the
            // 'index' of that cell in the Array to the function,
            // and simply return the index to create an Array of
            // all indices (there is almost certainly a better way
            // to do this):
            (cell, index) => index
          );
        }
      
        // here we retrieve the <tr> elements, using document.querySelectorAll(),
        // and use Array.from() to convert that NodeList into an Array of element
        // nodes:
        let rows = Array.from(
            settings.table.querySelectorAll('tr')
      
          // we then slice that collection from the supplied, or default,
          // settings.rowIndexStartAt to retrieve the relevant subset of
          // rows (note that this remains naive, since there's no check
          // that such an argument is supplied or available):
          ).slice(
            settings.rowIndexStartAt
      
          // we then use Array.prototype.map() on the Array returned from
          // Array.prototype.slice():
          ).map(
      
            // here we pass 'row' (the current HTMLTableRowElement of the
            // Array of HTMLTableRowElement nodes over which we're iterating)
            // to the Arrow function; in which create an Array of that row's
            // children (child elements) and filter that collection of children
            // using Array.prototype.filter():
            row => Array.from(row.children).filter(
      
              // here we pass both 'cell' (the current cell of the Array of
              // cells), and the'index' of that cell from the collection;
              // we then use Array.prototype.indexOf() to determine if the
              // current cell's index is in the Array of indices we wish to
              // keep. Array.prototype.indexOf() returns the index of the
              // supplied value if it was found, and -1 if it was not found;
              // therefore any returned index greater than -1 means the
              // supplied value was found, and should be kept in the filtered
              // Array:
              (cell, index) => settings.indices.indexOf(index) > -1
            )
          ),
          keyNames, headingSource, headings;
      
        // if an Array of keys were supplied, in order to assign custom
        // keys/names to identify the retrieved values/data:
        if (settings.keys && settings.keys.length > 0) {
      
          // keyNames is equal to the supplied keys:
          keyNames = settings.keys;
      
        // otherwise, if settings.namedByTable is (Boolean) true:
        } else if (settings.namedByTable === true) {
      
          // ...and settings.tableHeadRowIndex, parsed as a base-ten
          // integer, is equal-to or greater-than zero (zero is itself
          // a falsey value, so must be explicitly tested for):
          if (parseInt(settings.tableHeadRowIndex, 10) >= 0) {
      
            // we retrieve the settings.table HTMLTableElement:
            headingSource = settings.table
              // and retrieve the HTMLTableRowElement at the
              // index supplied in the settings.tableHeadRowIndex
              // argument:
              .rows[ settings.tableHeadRowIndex ]
              // and retrieve its child elements:
              .children;
      
          // otherwise, if the HTMLTableElement has a non-zero number of
          // <th> elements within a <thead> element:
          } else if (settings.table.querySelectorAll('thead th').length) {
      
            // we retrieve those elements:
            headingSource = settings.table.querySelectorAll('thead th')
      
          // otherwise, if the HTMLTableElement has a non-zero number of
          // <td> elements within a <thead> element:
          } else if (settings.table.querySelectorAll('thead td').length) {
      
            // we retrieve those elements:
            headingSource = settings.table.querySelectorAll('thead td')
      
          // otherwise, if the parsed integer of settings.rowIndexStartAt
          // in base-ten, is greater than zero:
          } else if (parseInt(settings.rowIndexStartAt, 10) > 0) {
      
            // we retrieve the HTMLTableRowElement at the index before the
            // the settings.rowIndexStartAt variable:
            headingSource = settings.table.rows[
              settings.rowIndexStartAt - 1 
            // and retrieve its child elements:
            ].children;
          }
      
          // here we convert the array-like headingSource variable
          // into an explicit Array (using Array.from):
          keyNames = Array.from(headingSource)
            // and then use Array.prototype.filter() to filter that
            // Array using an Arrow function:
            .filter(
              // here we do as we did earlier, and retain only those
              // elements whose index is found in the settings.indices
              // Array:
              (header, index) => settings.indices.indexOf(index) > -1
      
          // we then use Array.prototype.map() on the filtered Array:
          ).map(
      
            // and modify the Array to contain the trimmed text-content
            // of each cell:
            header => header.textContent.trim()
          )
      
        // otherwise:
        } else {
      
          // we simply use the numeric indices (if names cannot be found,
          // or appear to be unwanted):
          keyNames = settings.indices;
        }
      
        // here we iterate over the Array of HTMLTableRowElements:
        return rows.map(
      
          // pass the current row of the Array of rows:
                 // here we reduce the current HTMLTableRowElement
                 // and convert that array-like collection into an
                 // Array:
          row => Array.from(row)
      
            // reducing the Array in order to convert it to an Object:
            .reduce(function(accumulator, current, index) {
              // the accumulator is the Object literal supplied after
              // this anonymous function; and we set the key to identify
              // the current value (from the keyNames Array) to be the
              // key located at the same index as the current cell; and
              // we supply the textContent of the current cell as the
              // value associated with that key:
              accumulator[keyNames[index]] = current.textContent;
      
            // here we return the accumulator:
            return accumulator;
          }, {})
        );
      }
      
      let found = collateDataByIndices({
        indices: [0, 2, 4, 5]
      });
      
      console.log(found);
      

      function collateDataByIndices(opts) {
        let settings = {
          'table': document.querySelector('table'),
          'indices': null,
          'rowIndexStartAt': 1,
          'tableHeadRowIndex': 0,
          'namedByTable': true,
          'keys': false
        };
      
        Object.keys(opts || {}).forEach(
          key => settings[key] = opts[key]
        );
      
        if (!settings.indices || settings.indices.length === 0) {
          settings.indices = Array.from(
            settings.table.rows[0].cells
          ).map(
            (cell, index) => index
          );
        }
      
        let rows = Array.from(
            settings.table.querySelectorAll('tr')
          ).slice(
            settings.rowIndexStartAt
          ).map(
            row => Array.from(row.children).filter(
              (cell, index) => settings.indices.indexOf(index) > -1
            )
          ),
          keyNames, headingSource, headings;
      
        if (settings.keys && settings.keys.length > 0) {
          keyNames = settings.keys;
        } else if (settings.namedByTable === true) {
          if (parseInt(settings.tableHeadRowIndex, 10) >= 0) {
            headingSource = settings.table.rows[settings.tableHeadRowIndex].children
          } else if (settings.table.querySelectorAll('thead th').length) {
            headingSource = settings.table.querySelectorAll('thead th')
          } else if (settings.table.querySelectorAll('thead td').length) {
            headingSource = settings.table.querySelectorAll('thead td')
          } else if (parseInt(settings.rowIndexStartAt, 10) > 0) {
            headingSource = settings.table.rows[settings.rowIndexStartAt - 1].children;
          }
      
          keyNames = Array.from(headingSource).filter(
            (header, index) => settings.indices.indexOf(index) > -1
          ).map(
            header => header.textContent.trim()
          )
      
        } else {
          keyNames = settings.indices;
        }
      
      
        return rows.map(
          row => Array.from(row).reduce(function(accumulator, current, index) {
            accumulator[keyNames[index]] = current.textContent;
            return accumulator;
          }, {})
        );
      }
      
      let found = collateDataByIndices({
        indices: [0, 2, 4, 5]
      });
      
      console.log(found);
      <table width="100%">
        <thead>
          <tr>
            <td>Item Description</td>
            <td>Colour</td>
            <td>Size</td>
            <td class="text-right">Price</td>
            <td class="text-right">Qty</td>
            <td class="text-right">Total</td>
          </tr>
        </thead>
        <tbody>
          <tr id="row_24551">
            <td width="40%">Item 1</td>
            <td>Ivory</td>
            <td>10</td>
            <td class="text-right">$ 19.00</td>
            <td class="text-right">1</td>
            <td class="text-right">$ 19.00</td>
          </tr>
          <tr id="row_24550">
            <td width="40%">Item 2</td>
            <td>Grey Marle</td>
            <td>10</td>
            <td class="text-right">$ 18.95</td>
            <td class="text-right">1</td>
            <td class="text-right">$ 18.95</td>
          </tr>
        </tbody>
      </table>

      JS Fiddle demo.

      参考资料:

      【讨论】:

      • 这个解决方案很棒!但我认为我所追求的有点太复杂了。输出也很棒。我想我需要更深入地研究这个结构,谢谢
      【解决方案4】:

      这是一个仅使用 JavaScript 的解决方案。如果需要支持旧版浏览器,我可以重写。

      var rows = document.querySelectorAll('tr[id^="row"]');
      
      //const rowArray = Array.from(rows);
      var rowArray = Array.prototype.slice.call(rows);
      
      var products = rowArray.reduce((sum, element) => {
          var children = element.children;
          var name = children[0].innerHTML;
          var size = children[2].innerHTML;
          var quantity = children[4].innerHTML;
          var prize = children[5].innerHTML;
          var newElement = {name: name, size: size, quantity: quantity, prize: prize};
          sum.push(newElement);
          return sum;
      },[]);
      
      console.log(products);
      // [{name: "Item 1", size: "10", quantity: "1", prize: "$ 19.00"},
      // {name: "Item 2", size: "10", quantity: "1", prize: "$ 18.95"}]
      

      【讨论】:

      • 嗨 Pavlo,这在控制台中输出良好,但在现场运行时出现此错误:此语言功能仅支持 ECMASCRIPT6 或更好的模式:const 声明。使用 --language_in=ECMASCRIPT6 或 ECMASCRIPT6_STRICT 或更高版本来启用 ES6 功能。这是你提到的浏览器问题吗?
      • 对其进行了更新,使其不使用 ES6 功能,并且部分是的。如果还有更多错误,那么请尝试更改querySelectorAllrowArray.reduce,但应该不会发生
      • 谢谢@Pavlo!我将把它保存为 JS 替代品!感谢您的帮助。
      猜你喜欢
      • 1970-01-01
      • 2011-03-05
      • 1970-01-01
      • 2016-05-27
      • 1970-01-01
      • 1970-01-01
      • 2011-10-04
      相关资源
      最近更新 更多