Itérer à travers plusieurs éléments avec différents ID dans jQuery

Oct 23 2020

Je suis très nouveau dans jQuery et j'ai réussi à faire fonctionner ce code mais je suis presque sûr qu'il pourrait être fait de manière plus efficace. Par exemple, cela devrait être un moyen d'envoyer l'id en tant que paramètre au lieu d'écrire le même code pour chaque id.

Mais à côté de cela, y a-t-il d'autres choses qui pourraient être améliorées?

// selecting elements
$('#0') .find('.cart-quantity-input') .change(function () { var quantity = parseFloat($('#0').find('.cart-quantity-input').val());
    var price = parseFloat(
      $('#0').find('.cart-price').text().replace('€', '') ); $('#0')
      .find('.cart-subtotal')
      .text(quantity * price + '€');
  });

$('#1') .find('.cart-quantity-input') .change(function () { var quantity = parseFloat($('#1').find('.cart-quantity-input').val());
    var price = parseFloat(
      $('#1').find('.cart-price').text().replace('€', '') ); $('#1')
      .find('.cart-subtotal')
      .text(quantity * price + '€');
  });

$('#2') .find('.cart-quantity-input') .change(function () { var quantity = parseFloat($('#2').find('.cart-quantity-input').val());
    var price = parseFloat(
      $('#2').find('.cart-price').text().replace('€', '') ); $('#2')
      .find('.cart-subtotal')
      .text(quantity * price + '€');
  });

// compute subtotal sum
function subTotal(element) {
  var quantity = element.siblings('.cart-quantity-input').val();
  var price = parseFloat(
    element.closest('.cart-row').find('.cart-price').text().replace('€', '')
  );

  $(element) .closest('.cart-row') .find('.cart-subtotal') .text(price * quantity + '€'); } // compute total sum and number of items function computeTotalValues() { var total = [...$('.cart-items .cart-subtotal')]
    .map((subtotalElm) => Number(subtotalElm.textContent.replace('€', '')))
    .reduce((a, b) => a + b, 0);

  $('.cart-total-price').text(total + '€'); const totalQuantity = $.map(
    $('.cart-items .cart-quantity-input'), (input) => +input.value ).reduce((a, b) => a + b, 0); $('.items-number').text(totalQuantity + ' items');
}

// increase number of items
$('.plus').on('click', function () { var increment = parseInt($(this).siblings('input').val());

  increment++;
  $(this).siblings('input').val(increment); subTotal($(this));
  computeTotalValues();
});

// decrease number of items
$('.minus').on('click', function () { var decrement = parseInt($(this).siblings('input').val());

  if (decrement) {
    decrement--;
  }
  $(this).siblings('input').val(decrement); subTotal($(this));
  computeTotalValues();
});

// checkout button clicked
$('.btn-primary').on('click', function () { if ( confirm('Are you sure you want to checkout? \nYour products will disappear') ) { $('.cart-total-price').text('0 €');
    $('.items-number').text('0 items'); $('.cart-quantity-input').val('0');
  }
});
* {
  box-sizing: border-box;
  font-family: sans-serif;
  color: #777;
}

html,
body {
  margin: 0;
  padding: 0;
  min-height: 100%;
  background-color: #261741;
}

.container {
  display: flex;
  width: 55%;
  min-width: 700px;
  margin: 0 auto;
  border-radius: 10px;
  overflow: hidden;
  margin-top: 50px;
  min-height: 500px;
}

.first-section {
  background-color: #ffffff;
  position: relative;
  width: 70%;
  padding-left: 40px;
  padding-top: 10px;
}

.second-section {
  background-color: #f3f3f3;
  position: relative;
  width: 30%;
  padding: 10px 20px 20px 20px;
}

.section-header {
  font-weight: bold;
  color: #333;
  font-size: 18px;
}

.summary-title {
  display: flex;
}

.cart-items {
  margin-bottom: 60px;
}

.btn {
  text-align: center;
  vertical-align: middle;
  padding: 0.67em 0.67em;
  cursor: pointer;
}

.btn-primary {
  color: white;
  position: absolute;
  background-color: #9100ff;
  border: none;
  border-radius: 4px;
  font-weight: bold;
  display: block;
  font-size: 16px;
  bottom: 20px;
  width: 83%;
}

.btn-primary:hover {
  background-color: #000099;
}

.main-line {
  margin-right: 20px;
}

.cart-header {
  font-weight: bold;
  font-size: 15 px;
}

.cart-column {
  display: flex;
  align-items: center;
  color: black;
  margin-right: 1.5em;
  padding-bottom: 10px;
  margin-top: 10px;
}

.cart-column-title {
  display: flex;
  align-items: center;
  color: lightgray;
  margin-right: 1.5em;
  padding-bottom: 10px;
  margin-top: 10px;
}

.cart-row {
  display: flex;
}

.cart-item {
  width: 55%;
  font-size: 14px;
  text-transform: uppercase;
}

.cart-price {
  display: flex;
  justify-content: center;
  width: 15%;
  font-size: 14px;
  text-transform: uppercase;
}

.cart-quantity {
  display: flex;
  justify-content: center;
  width: 15%;
  font-size: 14px;
  text-transform: uppercase;
}

.cart-subtotal {
  display: flex;
  justify-content: center;
  width: 15%;
  font-size: 14px;
  text-transform: uppercase;
}

.item-info {
  display: flex;
  flex-direction: column;
}

.cart-item-title {
  color: #bc6cff;
  margin-left: 10px;
  font-size: 15px;
  font-weight: bold;
}

.cart-item-description {
  color: lightslategray;
  margin-left: 10px;
  font-size: 10px;
}

.cart-item-image {
  width: 75px;
  height: auto;
  border-radius: 10px;
}

.cart-quantity-input {
  height: 34px;
  width: 34px;
  border-radius: 5px;
  border: 2px solid lightgray;
  color: #333;
  padding: 0;
  text-align: center;
  font-size: 1.2em;
  margin-right: 15px;
  margin-left: 15px;
}

.cart-row:last-child .cart-column {
  border: none;
}

.cart-total {
  text-align: end;
  margin-top: 10px;
  margin-right: 10px;
}

.cart-total-title {
  font-size: 14px;
  color: black;
  margin-right: 20px;
  text-transform: uppercase;
}

.cart-total-price {
  color: #333;
  font-size: 1.1em;
}

.numerical-values {
  color: #bc6cff;
  font-size: 34px;
}

.numerical-values:hover {
  cursor: pointer;
}

.items-number {
  display: flex;
}

.items-and-total {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  padding: 17px 0px 0px 17px;
}

.items-and-total-final {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  padding: 0px 0px 0px 0px;
}

.items-discount {
  display: flex;
  text-transform: uppercase;
  padding-top: 15px;
}

.final-part {
  margin-bottom: 70px;
  bottom: 0px;
  position: absolute;
  width: 82%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!DOCTYPE html>
<html>
  <head>
    <title>Shopping cart</title>
    <meta name="description" content="This is the description" />
    <link rel="stylesheet" href="styles.css" />
  </head>
  <body>
    <div class="container">
      <div class="first-section">
        <h2 class="section-header">Shopping cart</h2>
        <hr class="main-line" />
        <div class="cart-row">
          <span class="cart-item cart-header cart-column-title"
            >Product details</span
          >
          <span class="cart-quantity cart-header cart-column-title"
            >Quantity</span
          >
          <span class="cart-price cart-header cart-column-title">Price</span>
          <span class="cart-subtotal cart-header cart-column-title">Total</span>
        </div>
        <div class="cart-items">
          <div class="cart-row" id="0">
            <div class="cart-item cart-column">
              <img
                class="cart-item-image"
                src="Images/goku.png"
                width="100"
                height="100"
              />
              <div class="item-info">
                <span class="cart-item-title">Goku POP</span>
                <span class="cart-item-description">Product code GOKU</span>
              </div>
            </div>
            <div class="cart-quantity cart-column">
              <div class="numerical-values minus">-</div>
              <input
                class="cart-quantity-input"
                disabled
                type="text"
                value="0"
              />
              <div class="numerical-values plus">+</div>
            </div>
            <span class="cart-price cart-column">5€</span>
            <span class="cart-subtotal cart-column">0€</span>
          </div>
          <div class="cart-row" id="1">
            <div class="cart-item cart-column">
              <img
                class="cart-item-image"
                src="Images/naruto.png"
                width="100"
                height="100"
              />
              <div class="item-info">
                <span class="cart-item-title">Naruto POP</span>
                <span class="cart-item-description">Product code NARUTO</span>
              </div>
            </div>
            <div class="cart-quantity cart-column">
              <div class="numerical-values minus">-</div>
              <input
                class="cart-quantity-input"
                id="naruto"
                disabled
                type="text"
                value="0"
              />
              <div class="numerical-values plus">+</div>
            </div>
            <span class="cart-price cart-column">20€</span>
            <span class="cart-subtotal cart-column">0€</span>
          </div>
          <div class="cart-row" id="2">
            <div class="cart-item cart-column">
              <img
                class="cart-item-image"
                src="Images/luffy.png"
                width="100"
                height="100"
              />
              <div class="item-info">
                <span class="cart-item-title">Luffy POP</span>
                <span class="cart-item-description">Product code LUFFY</span>
              </div>
            </div>
            <div class="cart-quantity cart-column">
              <div class="numerical-values minus">-</div>
              <input
                class="cart-quantity-input"
                id="luffy"
                disabled
                type="text"
                value="0"
              />
              <div class="numerical-values plus">+</div>
            </div>
            <span class="cart-price cart-column">7.5€</span>
            <span class="cart-subtotal cart-column">0€</span>
          </div>
        </div>
      </div>
      <div class="second-section">
        <div class="cart-total">
          <h2 class="section-header summary-title">Order Summary</h2>
          <hr />
          <div class="items-and-total">
            <span class="items-number">0 items</span>
            <span class="cart-total-price">0€</span>
          </div>
          <hr />
          <div class="final-part">
            <hr />
            <div class="items-and-total-final">
              <div class="cart-total-title">Total cost</div>
              <span class="cart-total-price">0€</span>
            </div>
          </div>
        </div>
        <button class="btn btn-primary" type="button">Checkout</button>
      </div>
    </div>
  </body>
  <script
    src="https://code.jquery.com/jquery-3.5.1.min.js"
    integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0="
    crossorigin="anonymous"
  ></script>
  <script src="store.js" async></script>
</html>

Réponses

3 CertainPerformance Oct 23 2020 at 07:35

Séchage

  • Les ID séparés à index numérique sont utilisés uniquement pour accéder à leurs .cart-quantity-inputéléments enfants , vous pouvez donc simplement sélectionner ces éléments directement à la place et supprimer les ID. Lorsque vous devez sélectionner autre chose dans la même ligne, utilisez d'abord:

    const $row = $(this).closest('.cart-row');
    

    et vous accéderez à la ligne à partir de laquelle vous pourrez, avec .find, accéder au prix, à la quantité et au total des éléments, selon vos besoins.

    Cela dit, étant donné uniquement le code de la question, le gestionnaire de modifications semble être entièrement superflu, car les boutons plus et moins calculent et rendent déjà les nouveaux sous-totaux d'élément - il ne semble pas nécessaire d'avoir un gestionnaire distinct en haut de ceux-ci, à moins qu'une autre partie de la page puisse la modifier également.

  • De plus, plutôt que d'avoir un gestionnaire distinct pour les boutons plus et moins, vous pouvez les combiner en un seul gestionnaire à la place, et vérifier la classe de l'élément cliqué pour déterminer s'il faut en ajouter ou en soustraire un de la quantité actuelle:

    $('.plus, .minus').on('click', function () { const currentValue = parseInt($(this).siblings('input').val());
      const addValue = $(this).is('.plus') ? 1 : -1; $(this).siblings('input').val(Math.max(0, currentValue + addValue));
    

Autres suggestions:

Les boutons +et -sont actuellement sélectionnables , ce qui semble étrange si l'on clique plus d'une fois de suite rapidement. Ils agissent comme des boutons, pas comme du texte brut, donc avoir le texte dans le bouton ne peut pas être sélectionné. Pensez à ajouter:

.numerical-values {
  user-select: none;
}

Après le contenu? Plutôt que d'avoir à ajouter et remplacer €chaque fois que vous souhaitez définir ou obtenir un prix, envisagez d'utiliser à la :afterplace:

.cart-price:after, .cart-subtotal:after {
  content: "€";
}

Noms précis Imaginez que vous n'aviez jamais vu le code auparavant et que vous ayez vu cette ligne: subTotal($(this));Que fait-il? Ce n'est pas très clair. Pensez à l'appeler quelque chose de plus précis, comme calculateAndRenderSubtotal, en rendant également le commentaire au-dessus de la fonction entièrement superflu:

// (Current code is:)

// compute subtotal sum
function subtotal(element) {

Utilisez une syntaxe moderne partout Vous utilisez ES2015 à plusieurs endroits du code. Si vous voulez l'utiliser (ce que vous devriez, cela rend les choses lisibles et concises!), Mieux vaut utiliser la syntaxe ES2015 + partout où vous le pouvez - en particulier, utilisez à la constplace de var(ou, utilisez letlorsque la variable doit être réaffectée).

Bug de paiement? Vous avez:

$('.btn-primary').on('click', function () {
  if (
    confirm('Are you sure you want to checkout? \nYour products will disappear')
  ) {
    $('.cart-total-price').text('0 €'); $('.items-number').text('0 items');
    $('.cart-quantity-input').val('0');
  }
});

Mais cela n'efface pas les sous-totaux de l'article. Remettez-les tous à 0.

$('.cart-subtotal').text(0);

Pensez également à remplacer le confirmpar un modal approprié - les boîtes d'alerte intégrées du navigateur bloquent le navigateur, empêchant JavaScript de s'exécuter sur la page et rendant la page inaccessible jusqu'à ce que la case soit effacée. Ce n'est pas convivial.

Combine select +.find I refactored it out while DRYing, mais chaque fois que vous avez du code comme:

$('#0')
  .find('.cart-quantity-input')

Cela simplifie à:

$('#0 .cart-quantity-input')

en utilisant un espace, le combinateur descendant .

Nouveau code suggéré:

/*
// This section can be removed entirely if 
// calculateAndRenderSubtotal is the only place
// where a row's quantity can change from

$('.cart-quantity-input').on('change', function() { const quantity = this.value; const $row = $(this).closest('.cart-row'); const price = $row.find('.cart-price').text();
  $row.find('.cart-subtotal').text(quantity * price); }); */ function calculateAndRenderSubtotal(element) { const quantity = element.siblings('.cart-quantity-input').val(); const price = parseFloat( element.closest('.cart-row').find('.cart-price').text() ); $(element)
    .closest('.cart-row')
    .find('.cart-subtotal')
    .text(price * quantity);
}

// compute total sum and number of items
function computeTotalValues() {
  const total = [...$('.cart-items .cart-subtotal')] .map((subtotalElm) => Number(subtotalElm.textContent)) .reduce((a, b) => a + b, 0); $('.cart-total-price').text(total);

  const totalQuantity = $.map( $('.cart-items .cart-quantity-input'),
    (input) => +input.value
  ).reduce((a, b) => a + b, 0);

  $('.items-number').text(totalQuantity + ' items'); } // increase number of items $('.plus, .minus').on('click', function() {
  const currentValue = parseInt($(this).siblings('input').val()); const addValue = $(this).is('.plus') ? 1 : -1;
  $(this).siblings('input').val(Math.max(0, currentValue + addValue)); calculateAndRenderSubtotal($(this));
  computeTotalValues();
});

// checkout button clicked
$('.btn-primary').on('click', function() { if ( confirm('Are you sure you want to checkout? \nYour products will disappear') ) { $('.cart-total-price').text('0');
    $('.items-number').text('0 items'); $('.cart-quantity-input').val('0');
    $('.cart-subtotal').text(0);
  }
});
* {
  box-sizing: border-box;
  font-family: sans-serif;
  color: #777;
}

html,
body {
  margin: 0;
  padding: 0;
  min-height: 100%;
  background-color: #261741;
}

.container {
  display: flex;
  width: 55%;
  min-width: 700px;
  margin: 0 auto;
  border-radius: 10px;
  overflow: hidden;
  margin-top: 50px;
  min-height: 500px;
}

.first-section {
  background-color: #ffffff;
  position: relative;
  width: 70%;
  padding-left: 40px;
  padding-top: 10px;
}

.second-section {
  background-color: #f3f3f3;
  position: relative;
  width: 30%;
  padding: 10px 20px 20px 20px;
}

.section-header {
  font-weight: bold;
  color: #333;
  font-size: 18px;
}

.summary-title {
  display: flex;
}

.cart-items {
  margin-bottom: 60px;
}

.btn {
  text-align: center;
  vertical-align: middle;
  padding: 0.67em 0.67em;
  cursor: pointer;
}

.btn-primary {
  color: white;
  position: absolute;
  background-color: #9100ff;
  border: none;
  border-radius: 4px;
  font-weight: bold;
  display: block;
  font-size: 16px;
  bottom: 20px;
  width: 83%;
}

.btn-primary:hover {
  background-color: #000099;
}

.main-line {
  margin-right: 20px;
}

.cart-header {
  font-weight: bold;
  font-size: 15 px;
}

.cart-column {
  display: flex;
  align-items: center;
  color: black;
  margin-right: 1.5em;
  padding-bottom: 10px;
  margin-top: 10px;
}

.cart-column-title {
  display: flex;
  align-items: center;
  color: lightgray;
  margin-right: 1.5em;
  padding-bottom: 10px;
  margin-top: 10px;
}

.cart-row {
  display: flex;
}

.cart-item {
  width: 55%;
  font-size: 14px;
  text-transform: uppercase;
}

.cart-price:after,
.cart-subtotal:after {
  content: "€";
}

.cart-price {
  display: flex;
  justify-content: center;
  width: 15%;
  font-size: 14px;
  text-transform: uppercase;
}

.cart-quantity {
  display: flex;
  justify-content: center;
  width: 15%;
  font-size: 14px;
  text-transform: uppercase;
}

.cart-subtotal {
  display: flex;
  justify-content: center;
  width: 15%;
  font-size: 14px;
  text-transform: uppercase;
}

.item-info {
  display: flex;
  flex-direction: column;
}

.cart-item-title {
  color: #bc6cff;
  margin-left: 10px;
  font-size: 15px;
  font-weight: bold;
}

.cart-item-description {
  color: lightslategray;
  margin-left: 10px;
  font-size: 10px;
}

.cart-item-image {
  width: 75px;
  height: auto;
  border-radius: 10px;
}

.cart-quantity-input {
  height: 34px;
  width: 34px;
  border-radius: 5px;
  border: 2px solid lightgray;
  color: #333;
  padding: 0;
  text-align: center;
  font-size: 1.2em;
  margin-right: 15px;
  margin-left: 15px;
}

.cart-row:last-child .cart-column {
  border: none;
}

.cart-total {
  text-align: end;
  margin-top: 10px;
  margin-right: 10px;
}

.cart-total-title {
  font-size: 14px;
  color: black;
  margin-right: 20px;
  text-transform: uppercase;
}

.cart-total-price {
  color: #333;
  font-size: 1.1em;
}

.numerical-values {
  color: #bc6cff;
  font-size: 34px;
  user-select: none;
}

.numerical-values:hover {
  cursor: pointer;
}

.items-number {
  display: flex;
}

.items-and-total {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  padding: 17px 0px 0px 17px;
}

.items-and-total-final {
  display: flex;
  flex-direction: row;
  justify-content: space-between;
  padding: 0px 0px 0px 0px;
}

.items-discount {
  display: flex;
  text-transform: uppercase;
  padding-top: 15px;
}

.final-part {
  margin-bottom: 70px;
  bottom: 0px;
  position: absolute;
  width: 82%;
}
<div class="container">
  <div class="first-section">
    <h2 class="section-header">Shopping cart</h2>
    <hr class="main-line" />
    <div class="cart-row">
      <span class="cart-item cart-header cart-column-title">Product details</span
          >
          <span class="cart-quantity cart-header cart-column-title"
            >Quantity</span
          >
          <span class="cart-price cart-header cart-column-title">Price</span>
      <span class="cart-subtotal cart-header cart-column-title">Total</span>
    </div>
    <div class="cart-items">
      <div class="cart-row" id="0">
        <div class="cart-item cart-column">
          <img class="cart-item-image" src="Images/goku.png" width="100" height="100" />
          <div class="item-info">
            <span class="cart-item-title">Goku POP</span>
            <span class="cart-item-description">Product code GOKU</span>
          </div>
        </div>
        <div class="cart-quantity cart-column">
          <div class="numerical-values minus">-</div>
          <input class="cart-quantity-input" disabled type="text" value="0" />
          <div class="numerical-values plus">+</div>
        </div>
        <span class="cart-price cart-column">5</span>
        <span class="cart-subtotal cart-column">0</span>
      </div>
      <div class="cart-row" id="1">
        <div class="cart-item cart-column">
          <img class="cart-item-image" src="Images/naruto.png" width="100" height="100" />
          <div class="item-info">
            <span class="cart-item-title">Naruto POP</span>
            <span class="cart-item-description">Product code NARUTO</span>
          </div>
        </div>
        <div class="cart-quantity cart-column">
          <div class="numerical-values minus">-</div>
          <input class="cart-quantity-input" id="naruto" disabled type="text" value="0" />
          <div class="numerical-values plus">+</div>
        </div>
        <span class="cart-price cart-column">20</span>
        <span class="cart-subtotal cart-column">0</span>
      </div>
      <div class="cart-row" id="2">
        <div class="cart-item cart-column">
          <img class="cart-item-image" src="Images/luffy.png" width="100" height="100" />
          <div class="item-info">
            <span class="cart-item-title">Luffy POP</span>
            <span class="cart-item-description">Product code LUFFY</span>
          </div>
        </div>
        <div class="cart-quantity cart-column">
          <div class="numerical-values minus">-</div>
          <input class="cart-quantity-input" id="luffy" disabled type="text" value="0" />
          <div class="numerical-values plus">+</div>
        </div>
        <span class="cart-price cart-column">7.5</span>
        <span class="cart-subtotal cart-column">0</span>
      </div>
    </div>
  </div>
  <div class="second-section">
    <div class="cart-total">
      <h2 class="section-header summary-title">Order Summary</h2>
      <hr />
      <div class="items-and-total">
        <span class="items-number">0 items</span>
        <span class="cart-total-price">0€</span>
      </div>
      <hr />
      <div class="final-part">
        <hr />
        <div class="items-and-total-final">
          <div class="cart-total-title">Total cost</div>
          <span class="cart-total-price">0€</span>
        </div>
      </div>
    </div>
    <button class="btn btn-primary" type="button">Checkout</button>
  </div>
</div>
</body>
<script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>