Warkus
Warkus

Reputation: 64

JavaScript - target a specific element, knowing only its class

I am wondering how to target a specific element, knowing only its class.

I want to click the .editPrice button and only showing the .popupPriceContainer next to the button and not all of them.

The close button works fine since it is a child of the element I want to hide and I can easily target it with parentNode.

Here is a fiddle: https://jsfiddle.net/jwLf4smu/1/

And Here a runnable snippet:

//CLOSE POPUP PRICE CONTAINER

//create variable from pop up close buttons
const popupClose = document.getElementsByClassName('popupClose');

//set price edit container to display none
Array.from(popupClose).forEach((popupClose) => {
  popupClose.addEventListener('click', () => {
    // 2x .parentNode to select the outer wrapper
    popupClose.parentNode.parentNode.style.display = "none";
  });
});


//OPEN POPUP PRICE CONTAINER

//create variable for all edit price buttons and price edit pop up container
const popupEditPrice = document.getElementsByClassName('editPrice');
const priceContainer = document.getElementsByClassName('popupPriceContainer');

//show price edit box
Array.from(popupEditPrice).forEach((popupEditPrice) => {
  popupEditPrice.addEventListener('click', () => {

    Array.from(priceContainer).forEach((priceContainer) => {
      priceContainer.style.display = "block";
    });

  });
});
#serviceList .position {
  margin-top: 16px;
}

.popupPriceContainer {
    display: none;
}
<section id="serviceList" class="items">

  <div class="position">
    <button type="button" class="editPrice">Edit price</button>

    <div class="item">
      Service 1
    </div>

    <div class="price">
      <span class="amount" id="priceElement">100</span>
    </div>

    <div class="popupPriceContainer">
      <div class="popupPriceInner">
        <input type="number" placeholder="100,00" class="priceInput">
        <button value="Set Value" class="submbitPrice popupClose">Submit</button>
        <button class="popupClose">Close</button>
      </div>
    </div>

  </div>

  <div class="position">
    <button type="button" class="editPrice">Edit price</button>

    <div class="item">
      Service 2
    </div>

    <div class="price">
      <span class="amount">1000</span>
    </div>

    <div class="popupPriceContainer">
      <div class="popupPriceInner">
        <input type="number" placeholder="100,00" class="priceInput">
        <button value="Set Value" class="submbitPrice popupClose">Submit</button>
        <button class="popupClose">Close</button>
      </div>
    </div>

  </div>

  <div class="position">
    <button type="button" class="editPrice">Edit price</button>

    <div class="item">
      Service 3
    </div>

    <div class="price">
      <span class="amount">240</span>
    </div>

    <div class="popupPriceContainer">
      <div class="popupPriceInner">
        <input type="number" placeholder="100,00" class="priceInput">
        <button value="Set Value" class="submbitPrice popupClose">Submit</button>
        <button class="popupClose">Close</button>
      </div>
    </div>

  </div>

</section>

Upvotes: 0

Views: 99

Answers (3)

Mister Jojo
Mister Jojo

Reputation: 22320

prefer to use a class:

.noDisplay {
  display: none;
  }

then use [element].classList / .add() or .remove() methods

full code:

const
  editBtns  = document.querySelectorAll('button.editPrice')
, closeBtns = document.querySelectorAll('button.popupClose')
  ;
editBtns.forEach(btn=>
  {
  btn.onclick = ({target}) =>  // get only target object in event 
    {
    target.closest('div.position').querySelector('.popupPriceContainer').classList.remove('noDisplay')
    }
  })
closeBtns.forEach(btn=>
  {
  btn.onclick = ({target}) =>  // get only target object in event 
    {
    let 
      div_position            = target.closest('div.position')
      box_popupPriceContainer = div_position.querySelector('.popupPriceContainer')
    , price_El                = box_popupPriceContainer.querySelector('input.priceInput')
    , actual_amount_el        = div_position.querySelector('span.amount')
      ;
    if (target.matches('.submbitPrice'))
      {
      // do submit price stuff
      let priceVal = parseInt( price_El.value.replace(/\D/g, ''),10) || 0;  // otherwise use parseFloat()
 
      price_El.value = priceVal;
      actual_amount_el.textContent = priceVal

      // do submit price  other stuff 
      }
    // else price_El.value = actual_amount_el.textContent 

    box_popupPriceContainer.classList.add('noDisplay')
    }
  })
#serviceList .position {
  margin-top: 16px;
  }
.noDisplay {
  display: none;
  }
<section id="serviceList" class="items">

  <div class="position">
    <button type="button" class="editPrice">Edit price</button>

    <div class="item">
      Service 1
    </div>

    <div class="price">
      <span class="amount" id="priceElement">100</span>
    </div>

    <div class="popupPriceContainer noDisplay">
      <div class="popupPriceInner">
        <input type="number" placeholder="100,00" class="priceInput">
        <button value="Set Value" class="submbitPrice popupClose">Submit</button>
        <button class="popupClose">Close</button>
      </div>
    </div>

  </div>

  <div class="position">
    <button type="button" class="editPrice">Edit price</button>

    <div class="item">
      Service 2
    </div>

    <div class="price">
      <span class="amount">1000</span>
    </div>

    <div class="popupPriceContainer noDisplay">
      <div class="popupPriceInner">
        <input type="number" placeholder="100,00" class="priceInput">
        <button value="Set Value" class="submbitPrice popupClose">Submit</button>
        <button class="popupClose">Close</button>
      </div>
    </div>

  </div>

  <div class="position">
    <button type="button" class="editPrice">Edit price</button>

    <div class="item">
      Service 3
    </div>

    <div class="price">
      <span class="amount">240</span>
    </div>

    <div class="popupPriceContainer noDisplay">
      <div class="popupPriceInner">
        <input type="number" placeholder="100,00" class="priceInput">
        <button value="Set Value" class="submbitPrice popupClose">Submit</button>
        <button class="popupClose">Close</button>
      </div>
    </div>

  </div>

</section>

but it should be better to use a delegate method way (get the global click over the section, then determine wich button is clicked) I also changed your Edit price / Close Price method, to put them on the same button, it is more ergonomic

code:

document.querySelector('#serviceList').onclick = ({target}) =>  // get only target object in event 
  {
  if (!target.matches('button.editPrice, button.submbitPrice')) return // ignore click elsewhere
 
  let 
    div_position            = target.closest('div.position')
  , price_El                = div_position.querySelector('input.priceInput')
  , actual_amount_el        = div_position.querySelector('span.amount')
    ;
  if (target.matches('button.editPrice'))
    {
    target.textContent = div_position.classList.toggle('noPriceEdit') ? 'Edit price' : 'Close edit'
    }
  else // button.submbitPrice stuff
    {
    let priceVal = parseFloat( price_El.value) || 0;  
    price_El.value = actual_amount_el.textContent = priceVal.toFixed(2)
 
    div_position.classList.add('noPriceEdit')
    div_position.querySelector('button.editPrice').textContent = 'Edit price'
    }
  }
#serviceList .position {
  margin-top: 16px;
  }
div.position.noPriceEdit > div.popupPriceContainer {
  display: none;
}
<section id="serviceList" class="items">
  <div class="position noPriceEdit">
    <button type="button" class="editPrice">Edit price</button>
    <div class="item">Service 1</div>
    <div class="price"><span class="amount" id="priceElement">100</span></div>
    <div class="popupPriceContainer noDisplay">
      <div class="popupPriceInner">
        <input type="number" placeholder="100,00" class="priceInput">
        <button value="Set Value" class="submbitPrice">Submit</button>
      </div>
    </div>
  </div>
  <div class="position noPriceEdit">
    <button type="button" class="editPrice">Edit price</button>
    <div class="item">Service 2</div>
    <div class="price"><span class="amount">1000</span></div>
    <div class="popupPriceContainer noDisplay">
      <div class="popupPriceInner">
        <input type="number" placeholder="100,00" class="priceInput">
        <button value="Set Value" class="submbitPrice">Submit</button>
      </div>
    </div>
  </div>
  <div class="position noPriceEdit">
    <button type="button" class="editPrice">Edit price</button>
    <div class="item">Service 3</div>
    <div class="price"><span class="amount">240</span></div>
    <div class="popupPriceContainer noDisplay">
      <div class="popupPriceInner">
        <input type="number" placeholder="100,00" class="priceInput">
        <button value="Set Value" class="submbitPrice">Submit</button>
      </div>
    </div>
  </div>
</section>

Upvotes: 2

mplungjan
mplungjan

Reputation: 178011

Delegate to the main static container and use e.target and relative addressing: e.target.closest('.popupPriceContainer')

Note I add and remove a class .hide

document.getElementById('serviceList').addEventListener('click', function(e) {
  const tgt = e.target;
  if (tgt.classList.contains('popupClose')) {
    const parent = tgt.closest('.popupPriceContainer')
    parent.classList.add('hide')
  }
  if (tgt.classList.contains('editPrice')) {
    const parent = tgt.closest('.position')
    parent.querySelector('.popupPriceContainer').classList.remove('hide')
  }
})
#serviceList .position {
  margin-top: 16px;
}

.hide {
  display: none;
}
<section id="serviceList" class="items">

  <div class="position">
    <button type="button" class="editPrice">Edit price</button>

    <div class="item">
      Service 1
    </div>

    <div class="price">
      <span class="amount" id="priceElement">100</span>
    </div>

    <div class="popupPriceContainer hide">
      <div class="popupPriceInner">
        <input type="number" placeholder="100,00" class="priceInput">
        <button value="Set Value" class="submbitPrice popupClose">Submit</button>
        <button class="popupClose">Close</button>
      </div>
    </div>

  </div>

  <div class="position">
    <button type="button" class="editPrice">Edit price</button>

    <div class="item">
      Service 2
    </div>

    <div class="price">
      <span class="amount">1000</span>
    </div>

    <div class="popupPriceContainer hide">
      <div class="popupPriceInner">
        <input type="number" placeholder="100,00" class="priceInput">
        <button value="Set Value" class="submbitPrice popupClose">Submit</button>
        <button class="popupClose">Close</button>
      </div>
    </div>

  </div>

  <div class="position">
    <button type="button" class="editPrice">Edit price</button>

    <div class="item">
      Service 3
    </div>

    <div class="price">
      <span class="amount">240</span>
    </div>

    <div class="popupPriceContainer hide">
      <div class="popupPriceInner">
        <input type="number" placeholder="100,00" class="priceInput">
        <button value="Set Value" class="submbitPrice popupClose">Submit</button>
        <button class="popupClose">Close</button>
      </div>
    </div>

  </div>

</section>

Upvotes: 1

Nice Books
Nice Books

Reputation: 1861

The problem is in the following code:

 popupEditPrice.addEventListener('click', () => {

     Array.from(priceContainer).forEach((priceContainer) => {
         priceContainer.style.display = "block";
     });

 });

When the "edit price" button is clicked the forEach in the above code searches for all price containers & shows them.

Each "edit price" button & its corresponding "price container" is inside a <div class="position">.

  1. Declare an event paramenter for the click listener
  2. Get a reference to the clicked price button using event.currentTarget
  3. Get a reference to the corresponding .position using closest method
  4. Find the .popupPriceContainer inside the div.position using querySelector
  5. Show the .popupPriceContainer using display block.

Upvotes: 0

Related Questions