简体   繁体   English

为什么我的 JavaScript forEach 重复两个元素然后 setAttribute 值到第三个元素代码只运行一次?

[英]Why does my JavaScript forEach muliplication of two elements then setAttribute value to a third element code only run once?

Background: I am creating a simple html order form for around 30 items using pure vanilla JavaScript ( no jQuery) and basic CSS.背景:我正在使用纯原版 JavaScript(jQuery)和基本的 CSS 为大约 30 件商品创建一个简单的 html 订单。 It will not involve any online payments.它不会涉及任何在线支付。 New items may be added by my client and they will simply copy paste a block of code for an existing item in the html of the article and give it a new item name.我的客户可能会添加新项目,他们会简单地将现有项目的代码块复制粘贴到文章的 html 中,并给它一个新的项目名称。 They will not have access to the JavaScript.他们将无法访问 JavaScript。 To avoid potential unwanted duplicates I am using only CSS classes and no element IDs.为了避免潜在的不需要的重复,我只使用 CSS 类并且没有元素 ID。 The form consists of a item name (text), a price and two input fields - one for quantity and one for total, with total being based dynamically on price*quantity as the form is filled.该表格由一个项目名称(文本)、一个价格和两个输入字段组成 - 一个用于数量,一个用于总计,在填写表格时,总计动态基于价格*数量。

My query: The quantity and form fields are pre-filled with zeros using a forEach loop and that works fine for all elements as expected.我的查询:数量和表单字段使用 forEach 循环预填充零,并且按预期对所有元素都有效。 What I cannot get to work is a second loop which calculates the total price based on price x quantity for anything other than the first one it finds.我无法开始工作的是第二个循环,它根据价格 x 数量计算总价,而不是它找到的第一个循环。

What am I missing?我错过了什么? Is it something to do with the onblur="calculate" command in the quantity input element?与数量输入元素中的onblur="calculate"命令有关吗? I have to admit I found this part in someone else's code and haven't used it before.我不得不承认我在别人的代码中发现了这部分并且之前没有使用过它。 Perhaps I should be using something else.也许我应该使用别的东西。

I have looked at other questions in a similar vein on here (and elsewhere) but they're either using jQuery or other types of JS or tables and so on.我在这里(和其他地方)以类似的方式查看了其他问题,但他们要么使用 jQuery 或其他类型的 JS 或表格等。

The code is below.代码如下。 TIA: TIA:

 if (document.querySelector('.order-form')) { let qty = document.querySelectorAll('.qty'); let total = document.querySelectorAll('.total'); [].forEach.call(qty, function(zero) { zero.setAttribute('value','0'); }); [].forEach.call(total, function(zero) { zero.setAttribute('value','0'); }) } calculate = function() { let qty = document.querySelectorAll('.qty'); [].forEach.call(qty, function() { let price = document.querySelector('.price').innerText; let newprice = parseFloat(price).toFixed(2); let qty = document.querySelector('.qty').value; let newtotal = parseFloat((newprice)*(qty)).toFixed(2); let total = document.querySelector('.total'); total.setAttribute('value',newtotal); }); }
 .order-form { margin: 16px; width: 100%; }.row { width: 100%; }.prod { margin-top: 16px; }.price { width: 10%; display: inline-block; }.price:before { content: "Price: £"; }.input-style { width: 70%; display: inline-block; } label { padding: 0 16px; }
 <div class="order-form"> <div class="prod">Subscription Receipt Book</div> <div class="row"> <div class="price" style="width: 10%; display: inline-block;">4.50</div> <div class="input-style"> <label for="qty">Qty:</label><input class="qty" type="text" name="qty" onblur="calculate()" /> <label for="total_amt">Total: £</label><input class="total" type="text" name="total_amt" /></div> </div> <div class="row" style="width: 100%;"> <div class="prod">General Receipt Book</div> <div class="price" style="width: 10%; display: inline-block;">4.00</div> <div class="input-style"> <label for="qty">Qty:</label><input class="qty" type="text" name="qty" onblur="calculate()" /> <label for="total_amt">Total: £</label><input class="total" type="text" name="total_amt" /> </div> </div> <div class="row" style="width: 100%;"> <div class="prod">Minutes book</div> <div class="price" style="width: 10%; display: inline-block;">5.50</div> <div class="input-style"> <label for="qty">Qty:</label><input class="qty" type="text" name="qty" onblur="calculate()" /> <label for="total_amt">Total: £</label><input class="total" type="text" name="total_amt" /> </div> </div> </div>

The issue is because you're selecting all the .price , .qty and .total elements in the DOM in the calculate() function, not only the ones in the same row as the field which was updated.问题是因为您在calculate() function 中选择了 DOM 中的所有.price.qty.total元素,而不仅仅是与已更新字段位于同一行的元素。

To fix this you can simply use closest() to get the .row element relative to the .qty which the user updated, and select the necessary fields from there.要解决此问题,您可以简单地使用closest()来获取相对于用户更新的.qty.row元素,并从那里获取必要的字段 select 。

Also note that there's several other improvements which should be made to your code.另请注意,您的代码还应进行其他几项改进。

  • Don't use inline event handlers in your HTML, eg.不要在 HTML 中使用内联事件处理程序,例如。 onblur , onclick etc. They are outdated and not good practice. onbluronclick等。它们已经过时并且不是好的做法。 Bind your events unobtrusively in the JS using addEventListener() instead.改为使用addEventListener()在 JS 中不显眼地绑定您的事件。
  • You can call forEach() directly on the collections returned by querySelectorAll() .您可以直接在querySelectorAll()返回的 collections 上调用forEach() ) 。 This is now well supported so you don't need to mess around converting them to arrays first.这现在得到了很好的支持,因此您无需先将它们转换为 arrays。
  • Set the value of the inputs at the time you generate them and append them to the DOM.在生成输入时设置输入value ,并将 append 设置为 DOM。 If you do this after the elements have been created it's likely that they will appear empty for a split second which is unsightly, and should be avoided.如果您在创建元素之后执行此操作,则它们可能会在一瞬间显得空无一物,这是难看的,应该避免。
  • The .total fields default value should be set to 2dp, ie. .total字段默认值应设置为 2dp,即。 0.00 , not just 0 . 0.00 ,而不仅仅是0
  • CSS should all be in an external stylesheet. CSS 都应该在外部样式表中。 Don't put it in your HTML in a style attribute.不要将它放在您的 HTML 中的style属性中。
  • Update the values of your inputs using the value property.使用value属性更新输入的值。 You don't need to use setAttribute() for this.您不需要为此使用setAttribute()
  • The for attributes on your label elements won't do anything as their values don't match the id of any of the inputs. label元素上的for属性不会做任何事情,因为它们的值与任何输入的id都不匹配。 To workaround this, remove the for attributes and wrap the label elements around the input .要解决此问题,请删除for属性并将label元素包裹在input周围。
  • Make the .total fields readonly , otherwise users can edit them and set whatever price they like..total字段设为readonly ,否则用户可以编辑它们并设置他们喜欢的任何价格。

With all that said, try this:说了这么多,试试这个:

 document.querySelectorAll('.qty').forEach(el => { el.addEventListener('input', e => { const row = e.target.closest('.row'); const qty = e.target.value; const price = row.querySelector('.price').textContent; row.querySelector('.total').value = parseFloat(price * qty).toFixed(2); }); });
 .order-form { margin: 16px; width: 100%; }.row { width: 100%; }.prod { margin-top: 16px; }.price { width: 10%; display: inline-block; }.price:before { content: "Price: £"; }.input-style { width: 70%; display: inline-block; } label { padding: 0 16px; }
 <div class="order-form"> <div class="prod">Subscription Receipt Book</div> <div class="row"> <div class="price">4.50</div> <div class="input-style"> <label> Qty: <input class="qty" type="text" name="qty" value="0" /> </label> <label> Total: £ <input class="total" type="text" name="total_amt" value="0.00" readonly /> </label> </div> </div> <div class="row"> <div class="prod">General Receipt Book</div> <div class="price">4.00</div> <div class="input-style"> <label> Qty: <input class="qty" type="text" name="qty" value="0" /> </label> <label> Total: £ <input class="total" type="text" name="total_amt" value="0.00" readonly /> </label> </div> </div> <div class="row"> <div class="prod">Minutes book</div> <div class="price">5.50</div> <div class="input-style"> <label> Qty: <input class="qty" type="text" name="qty" value="0" /> </label> <label> Total: £ <input class="total" type="text" name="total_amt" value="0.00" readonly /> </label> </div> </div> </div>

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM