遍历 html table 并计算值

Iterate through html table and calculates value

目标是人们应该在输入字段中输入一个值,然后 JavaScript 应该乘以一个固定值。

代码应为每一行执行此操作,并且无需刷新即可自动执行此操作。

JavaScript 适用于第一行,但如果我添加循环(前 3 行代码),它就不再适用了。

Table 看起来像这样

<table class="u-table-entity" id="table_gebäck">
  <script src="calculation.js"></script>
  <colgroup>
    <col width="20%">
    <col width="2.1%">
    <col width="22%">
    <col width="21.7%">
    <col width="34.2%">
  </colgroup>
  <tbody class="u-table-alt-grey-5 u-table-body">
    <tr style="height: 55px;">
      <b>
      <td class="u-table-cell u-table-cell-1"><b>Produkt</b><span style="font-weight: 700;"></span>
      </td>
      <td class="u-table-cell"></td>
      <td class="u-table-cell u-table-cell-3"><b>Einzelpreis</b></td>
      <td class="u-table-cell u-table-cell-4"><b>Menge</b></td>
      <td class="u-table-cell u-table-cell-5"><b>Gesamtpreis</b></td>
      </b>
    </tr>
    <tr style="height: 55px;">
      <td class="u-table-cell">Kornspitz</td>
      <td class="u-table-cell"></td>
      <td class="u-table-cell">
        <p value="1.39" id="basePrice">1,39 €</p>
      </td>
      <td class="u-table-cell">
        <form id="Menge">
          <input type="number" min="0" id="quantity" value="0" step="1.0">
        </form>
      </td>
      <td class="u-table-cell">
        <form id="sum">
          <p><output id="field_sum" for="quantity">0</output> €</p>
        </form>
      </td>
    </tr>
    <tr style="height: 55px;">
      <td class="u-table-cell">Row 2</td>
      <td class="u-table-cell"></td>
      <td class="u-table-cell">
        <p value="5.39" id="basePrice">5,39 €</p>
      </td>
      <td class="u-table-cell">
        <form id="Menge">
          <input type="number" min="0" id="quantity" value="0" step="1.0">
        </form>
      </td>
      <td class="u-table-cell">
        <form id="sum">
          <p><output id="field_sum" for="quantity">0</output> €</p>
        </form>
      </td>
    </tr>
  </tbody>
</table>

和我目前创建的 JavaScript 代码

var table = document.getElementById("table_gebäck");

for (var i = 0, row; row = table.rows[i]; i++) {
  let row = table.rows[i]
  (function () {
    const basePrice = document.getElementsByClassName("basePrice");
    const quantityInput = document.getElementsByClassName("quantity");
    const resOutput = document.getElementsByClassName("field_sum");
    quantityInput.addEventListener("change", function () {
      let currentQuantity = parseFloat(quantityInput.value);
      let currentBasePrice = parseFloat(basePrice.getAttribute("value"));
      resOutput.textContent = currentQuantity * currentBasePrice;
    });
  }());
}

编辑

这对我也有帮助

Calculating total price by iterating in the table using JavaScript

除了标记 cleanup/sanitize 任务之外,还可以实现一种 微组件 方法。

一个可行的解决方案也不可知(从)某个 DOM 层次结构可以基于 custom data attributes and dataset access together with bound handler data ...

function jsonParse(value) {
  let result = null;
  try {
    result = JSON.parse(value);
  } catch(exception) {}
  return result;
}
function sanitizeFloatValue(value) {
  value = parseFloat(value);
  return Number.isFinite(value) ? value : null;
}

function updateTotalValueFromBoundContextData(/* evt */) {
  const { source, target, baseValue, toFixed } = this;
  const totalValue = baseValue * (sanitizeFloatValue(source.value) || 0);

  target.value = (typeof toFixed === 'number')
    ? totalValue.toFixed(toFixed)
    : totalValue;
}
function initializeUpdateTotalValue(componentRoot) {
  const source = componentRoot
    .querySelector('[data-base-value]');
  const target = componentRoot
    .querySelector('[data-total-value]');

  const baseValue = sanitizeFloatValue(source.dataset.baseValue) || 0;

  const config = jsonParse(componentRoot.dataset.updateTotalValue) ?? {};
  const toFixed = sanitizeFloatValue(config.toFixed);

  const context = { source, target, baseValue, toFixed };

  source
    .addEventListener('input',
      updateTotalValueFromBoundContextData.bind(context)
    );

  // initial update call
  updateTotalValueFromBoundContextData.call(context);
}

function main() {
  document
    .querySelectorAll('[data-update-total-value]')
    .forEach(initializeUpdateTotalValue);
}
main();
body { zoom: .8; }
tr, td { margin: 0!important; padding: 0!important; height: unset!important; }
<form name="product-orders">
  <!-- <script src="calculation.js"></script> //-->
  <table class="u-table-entity">
    <colgroup>
      <col width="20%">
      <col width="2.1%">
      <col width="22%">
      <col width="21.7%">
      <col width="34.2%">
    </colgroup>
    <tbody class="u-table-alt-grey-5 u-table-body">
      <tr style="height: 55px;">
        <td class="u-table-cell u-table-cell-1"><b>Produkt</b><span style="font-weight: 700;"></span>
        </td>
        <td class="u-table-cell"></td>
        <td class="u-table-cell u-table-cell-3"><b>Einzelpreis</b></td>
        <td class="u-table-cell u-table-cell-4"><b>Menge</b></td>
        <td class="u-table-cell u-table-cell-5"><b>Gesamtpreis</b></td>
      </tr>
      <tr style="height: 55px;" data-update-total-value='{"toFixed": 2}'>
        <td class="u-table-cell">Kornspitz</td>
        <td class="u-table-cell"></td>
        <td class="u-table-cell">
          <p value="1.39">1,39 €</p>
        </td>
        <td class="u-table-cell">
          <input type="number"
            min="0" value="0" step="1"
            name="quantity-Kornspitz"
            id="quantity-Kornspitz"
            data-base-value="1.39"
          />
        </td>
        <td class="u-table-cell">
          <p>
            <output for="quantity-Kornspitz" data-total-value>0</output>
            €
          </p>
        </td>
      </tr>
      <tr style="height: 55px;" data-update-total-value='{"toFixed": 2}'>
        <td class="u-table-cell">Row 2</td>
        <td class="u-table-cell"></td>
        <td class="u-table-cell">
          <p value="5.39">5,39 €</p>
        </td>
        <td class="u-table-cell">
          <input type="number"
            min="0" value="0" step="1"
            name="quantity-Row_2"
            id="quantity-Row_2"
            data-base-value="5.39"
          />
        </td>
        <td class="u-table-cell">
          <p>
            <output for="quantity-Row_2" data-total-value>0</output>
            €
          </p>
        </td>
      </tr>
      <tr style="height: 55px;" data-update-total-value>
        <td class="u-table-cell">float number calculation</td>
        <td class="u-table-cell"></td>
        <td class="u-table-cell">
          <p value="1.39">1,39 €</p>
        </td>
        <td class="u-table-cell">
          <input type="number"
            min="0" value="5" step="1"
            name="quantity-Row_3"
            id="quantity-Row_3"
            data-base-value="1.39"
          />
        </td>
        <td class="u-table-cell">
          <p>
            <output for="quantity-Row_3" data-total-value>0</output>
            €
          </p>
        </td>
      </tr>
      <tr style="height: 55px;" data-update-total-value='{"toFixed": 4}'>
        <td class="u-table-cell"><code>toFixed</code> set to 4</td>
        <td class="u-table-cell"></td>
        <td class="u-table-cell">
          <p value="5.39">5,39 €</p>
        </td>
        <td class="u-table-cell">
          <input type="number"
            min="0" value="3" step="1"
            name="quantity-Row_4"
            id="quantity-Row_4"
            data-base-value="5.39"
          />
        </td>
        <td class="u-table-cell">
          <p>
            <output for="quantity-Row_4" data-total-value>0</output>
            €
          </p>
        </td>
      </tr>
    </tbody>
  </table>
</form>

编辑

"Thank you very much for sharing this But i have no idea how to use it – Setzi138"

好的,那我试试...

首先,OP 需要编写有效的标记,因此任何元素的 id 属性.

永远不会有重复值

其次,这个任务应该尽可能通用地解决。一个例如不应实施任何与特定 DOM 层次结构(或标记)或过于具体的任务(如计算总价)紧密耦合的代码。

因此,必须找到一种解决方案,该解决方案可以轻松适应具有与此处相同任务的其他环境,实际上是... “更新并显示根据基数计算的总值值和当前更改的乘数,每当此乘数值更改时

以上措辞严谨的要求,为解决方案指明了方向,甚至指明了基石。

主初始化任务首先需要识别每个结构体,然后才能初始化。

一个可行的方法是利用 custom data-* attributes instead of class (coupled to layout and styling) and id (too specific, disqualifies for generic solutions) attributes. In addition one can provide any string based data to a custom attribute which can be accessed via an HTMLElement's dataset 属性.

尽管该方法声称尽可能与标记无关,但它有一个先决条件。这种 “微组件” 的结构需要有一个根元素和两个其他元素,它们都需要嵌套 under/within 组件根。 一个元素是数量变化的触发器或来源,另一个是更新总值的目标。

利用 [data-*] 属性在 DOM 中访问这些元素也没有缺点,因为解决方案通过 <element >.querySelector and <element >.querySelectorAll.

使用现代元素查询

现在唯一剩下的就是正确的命名和实现本身。

...

+++待续+++

...

通过对第一个所示方法的更多迭代,人们最终可以提供基于模块的实现高度通用customizableself-initializing 组件,由于自定义功能,它也可以在 DOM 不允许大规模更改标记的环境中运行...

// utility functions
function parseJSON(value) {
  let result = null;
  try {
    result = JSON.parse(value);
  } catch(exception) {}
  return result;
}
function sanitizeFloatValue(value) {
  value = parseFloat(value);
  return Number.isFinite(value) ? value : null;
}

function getCustomKeyFromAttributeSelector(selector) {
  return (/\[\s*data-(?<customKey>[^\s\]]+)(?:\s|\])/)
    .exec(selector)
    ?.groups
    ?.customKey;
}
function getDatasetKeyFromCustomAttributeSelector(selector) {
  return getCustomKeyFromAttributeSelector(selector)
    ?.toLowerCase()
    ?.replace((/-(\p{Ll})/gu), (match, char) => char.toUpperCase());
}

// component specific module based code
const updateTotalValueModule = (function () {
  const DEFAULT_CONFIG = {
    calculation: {
      baseValue: 0,
      toFixed: 2,
    },
    selectors: {
      source: '[data-base-value]',
      target: '[data-total-value]',
    },
    eventType: 'input',
  }
  const DEFAULT_SELCTOR_COMPONENT = '[data-update-total-value]';

  function updateTotalValueFromBoundContextData(/* evt */) {
    const { source, target, baseValue, toFixed } = this;
    const totalValue = baseValue * (sanitizeFloatValue(source.value) || 0);

    target.value = (typeof toFixed === 'number')
      ? totalValue.toFixed(toFixed)
      : totalValue;
  }
  let currentConfigKey;

  function initializeUpdateTotalValue(componentRoot) {
    const { // config values.
      selectors = DEFAULT_CONFIG.selectors,
      eventType = DEFAULT_CONFIG.eventType,
      calculation = DEFAULT_CONFIG.calculation
    } = (parseJSON(
      componentRoot.dataset[currentConfigKey]
    ) ?? DEFAULT_CONFIG);

    const source = componentRoot.querySelector(
      selectors.source || DEFAULT_CONFIG.selectors.source
    );
    const target = source && componentRoot.querySelector(
      selectors.target || DEFAULT_CONFIG.selectors.target
    );
    if (source && target) {

      const baseValue = (
        calculation.baseValue ||
        sanitizeFloatValue(source.dataset[
          getDatasetKeyFromCustomAttributeSelector(
            selectors.source || DEFAULT_CONFIG.selectors.source
          )
        ]) || 0
      );
      const toFixed = sanitizeFloatValue(
        calculation.toFixed || DEFAULT_CONFIG.calculation.toFixed
      );
      const context = { source, target, baseValue, toFixed };

      source.addEventListener(
        eventType,
        updateTotalValueFromBoundContextData.bind(context)
      );

      // initial update call
      updateTotalValueFromBoundContextData.call(context);
    }
  }
  function initialize(customAttributeSelector) {
    const selector = (typeof customAttributeSelector === 'string')
      && customAttributeSelector
      || DEFAULT_SELCTOR_COMPONENT;

    currentConfigKey = getDatasetKeyFromCustomAttributeSelector(selector);

    document
      .querySelectorAll(selector)
      .forEach(initializeUpdateTotalValue);
  }

  // (module) export(s)
  return {
    initialize
  };

}());

// running the component ...

// ... does target any default-config conform component.
updateTotalValueModule.initialize();

// ... does target any custom specific component.
updateTotalValueModule.initialize('[data-auto-total-price]');
body { zoom: .8; }
tr, td { margin: 0!important; padding: 0!important; height: unset!important; }
<!-- <script src="calculation.js"></script> //-->
<table class="u-table-entity" id="table_gebäck">
  <colgroup>
    <col width="20%">
    <col width="2.1%">
    <col width="22%">
    <col width="21.7%">
    <col width="34.2%">
  </colgroup>
  <tbody class="u-table-alt-grey-5 u-table-body">
    <tr style="height: 55px;">
      <td class="u-table-cell u-table-cell-1"><b>Produkt</b><span style="font-weight: 700;"></span>
      </td>
      <td class="u-table-cell"></td>
      <td class="u-table-cell u-table-cell-3"><b>Einzelpreis</b></td>
      <td class="u-table-cell u-table-cell-4"><b>Menge</b></td>
      <td class="u-table-cell u-table-cell-5"><b>Gesamtpreis</b></td>
    </tr>
    <tr style="height: 55px;" data-update-total-value ='{"calculation":{"baseValue":1.39,"toFixed":2},"selectors":{"source":"form input[type=\"number\"]","target":"form output"},"eventType":"input"}'>
      <td class="u-table-cell">Kornspitz</td>
      <td class="u-table-cell"></td>
      <td class="u-table-cell">
        <p>1,39 €</p>
      </td>
      <td class="u-table-cell">
        <form name="amount_Kornspitz">
          <input type="number" value="0" min="0" step="1" id="amount_Kornspitz">
        </form>
      </td>
      <td class="u-table-cell">
        <form name="price_total_Kornspitz">
          <p>
            <output for="amount_Kornspitz">0</output>
            €
          </p>
        </form>
      </td>
    </tr>
    <tr style="height: 55px;" data-auto-total-price='{"calculation":{"toFixed":4},"selectors":{"target":"form output"}}'>
      <td class="u-table-cell">Row 2</td>
      <td class="u-table-cell"></td>
      <td class="u-table-cell">
        <p>5,39 €</p>
      </td>
      <td class="u-table-cell">
        <form name="amount_Row_2">
          <input type="number" value="5" min="0" step="1" id="amount_Row_2" data-base-value="5.39">
        </form>
      </td>
      <td class="u-table-cell">
        <form name="price_total_Row_2">
          <p>
            <output for="amount_Row_2">0</output>
            €
          </p>
        </form>
      </td>
    </tr>
  </tbody>
</table>