Mapping buttons to containers

I am not familiar with how this works, but this is what I have been told.

If anyone on here would like to give more input, or if there may be a better way to write this code, feel free to give as much information.

Is there a more simpler way to write this code?

Is there a better way to do this? A better way to write it?

Is any of it able to be improved?

Maybe it could be written using less code, I don’t know.

That could be done by adding a data-container attribute to each button which stores a class or an id of the container it should act upon. If you did that, you’d need a different class or id for each container (you already do that with play1 and play2 ). Add a data-container attribute to each button with its value as the corresponding container class (like for example: data-container="play1" to the .playa button and data-container="play2" to the .playb button) and replace cover.previousElementSibling.querySelector(".container") with document.getElementsByClassName(playButton.dataset.container)[0] .

First Solution: https://jsfiddle.net/6vughped/

 function showCover(playButton) {
    hideAll(config.containers);
    resetPage();
    markAsPlayed(playButton);
    const playButtonContainer = playButton.parentElement;
    const container = document.getElementsByClassName(playButton.dataset.container)[0];
    playButtonContainer.classList.add("active");
    container.classList.add("active");
    show(playButtonContainer);
    show(container);
  }
<div class="playButtonContainer with-curtain">
  <button class="playa cover" type="button" data-container="play1"></button>
  <button class="playb cover" type="button" data-container="play2"></button>
  <button class="playc cover" type="button" data-container="play3"></button>
</div>

Second Solution: https://jsfiddle.net/xhe4bczt/

 function showCover(playButton) {
    hideAll(config.containers);
    resetPage();
    markAsPlayed(playButton);
    const playButtonContainer = playButton.parentElement;
    const playButtonIndex = Array.from(document.getElementsByClassName('cover')).findIndex(element => element == playButton)
    const container = config.containers[playButtonIndex]
    playButtonContainer.classList.add("active");
    container.classList.add("active");
    show(playButtonContainer);
    show(container);
  }
<div class="playButtonContainer with-curtain">
  <button class="playa cover" type="button"></button>
  <button class="playb cover" type="button"></button>
</div>

There are 2 other ways, one which maps buttons to containers by getting the last character of the class which starts with play, like in playa it’s a and in play1 it’s 1, so it maps “a” to “1”, and the other by creating an object with keys as button classes mapping to values as container classes, like { playa: 'play1' }. Remember that for the first way, you can only have 26 containers named play1 to play26 and 26 buttons named playa to playz, and for the second way, whenever you add or remove a button and a container you need to update the object inside the arguments of manageCover.init call inside the init function of players.

First Way: https://jsfiddle.net/uxkdgsc0/

  function showCover(playButton) {
    hideAll(config.containers);
    resetPage();
    markAsPlayed(playButton);
    const playButtonContainer = playButton.parentElement;
    const playButtonClasses = playButton.className.split(' ')
    const playButtonPlayClass = playButtonClasses[playButtonClasses.findIndex(className => className.startsWith('play'))]
    const containerPlayClass = 'play' + (playButtonPlayClass[4].charCodeAt() - 96)
    const container = document.getElementsByClassName(containerPlayClass)[0]
    playButtonContainer.classList.add("active");
    container.classList.add("active");
    show(playButtonContainer);
    show(container);
  }
<div class="playButtonContainer with-curtain">
  <button class="playa cover" type="button"></button>
  <button class="playb cover" type="button"></button>
</div>

Second Way: https://jsfiddle.net/c1qnrxak/

  function showCover(playButton) {
    hideAll(config.containers);
    resetPage();
    markAsPlayed(playButton);
    const playButtonContainer = playButton.parentElement;
    const playButtonClasses = playButton.className.split(' ')
    let containerClass
    for(var className of playButtonClasses) {
    	containerClass = config.playButtonsToContainers[className]
      if (containerClass) break;
    }
    const container = document.getElementsByClassName(containerClass)[0]
    playButtonContainer.classList.add("active");
    container.classList.add("active");
    show(playButtonContainer);
    show(container);
  }
  function init(selectors) {
    config.playButtonsToContainers = selectors.playButtonsToContainers;
  }
function init() {
    manageCover.init({
      container: ".container",
      playButton: ".cover",
      playButtonsToContainers: {
        playa: 'play1',
        playb: 'play2',
        playc: 'play3',
        playd: 'play4',
        playe: 'play5',
        playf: 'play6',
        playg: 'play7',
        playh: 'play8',
        plaei: 'play9'
      }
    });
<div class="playButtonContainer with-curtain">
  <button class="playa cover" type="button"></button>
  <button class="playb cover" type="button"></button>
</div>
1 Like

I mentioned this to you a number of times now as a way of storing information about the destination of the clicked button and indeed 10,000 posts ago I gave you a working example but you decided to go the more complicated route and use the index of the button to find the same indexed container in order to find the player that way. An approach I see as fragile because it relies heavily on the mark up remaining the same forever which never happens in your code.

Indeed in one of my recent examples I also mentioned using a data attribute to store the destination and was included in a recent demo.

<button data-destination=".container1" class="exit" type="button" aria-label="Open"></button>
 <button data-destination=".container2" class="exit back" type="button" aria-label="Open"></button>

Of course your separate player button container method will not be as semantic or as accessible as the original where the play buttons are next to the player that they refer to. I mentioned this to you right at the start (thousands of posts ago) and the reason I gave you a method to have the play buttons appear together as a block while still remaining next to the player they refer to.

You ask if there is a ‘better way’ but that question makes no sense in light of your approach of just randomly changing things when you see something more sparkly to use :slight_smile: As you only seem to be concerned with doing things differently and just trying things out then that question can’t really be answered as no one knows what your final goal is.

I believe simpler is better and avoid tying your css and js to specific mark up where possible. Things like parentElement.parentElement are very hit and miss if you have added an extra div wrapper later on to create some extra styling. It would be better to find the parent element you need by its classname rather than expecting it to always be two levels up.

1 Like

I agree, the other ways look very clunky.

That can be used for this? But wasn’t it?

Only 1 button was able to be opened:
const container = cover.previousElementSibling.querySelector(".container");

For the 2 below, would the js use what was done in here?
https://jsfiddle.net/1o2nr8mx/

/* when container is active hide the cssPlay and show the inner container*/
.playButtonContainer.active {
  display: none;
}
    function showCover(playButton) {
    hideAll(config.containers);
    resetPage();
    markAsPlayed(playButton);
    const cover = playButton.parentElement;
    const container = cover.previousElementSibling.querySelector(".container");
    cover.classList.add("active");
    container.classList.add("active");
    show(cover);
    show(container);
  }

How would your way work on here then?

I just tried to place your code inside one of these and I got all messed up.

9 buttons If your way works on one of these, it would work on the other?
https://jsfiddle.net/p5qftaom/2/

1 button How would your way work on here? https://jsfiddle.net/c361twm7/1/

<div class="playButtonContainer with-curtain">
<button class="playa cover" type="button" aria-label="Open"></button>
</div>

How would your code be placed in here?

const manageUI = (function makeManageUI() {
  const body = document.body;

  const players = [];

  function findPlayers() {
    const allCovers = document.querySelectorAll(".cover");
    const allWrappers = document.querySelectorAll(".wrap");
    allCovers.forEach(function addToPlayers(cover, index) {
      players.push({
        "cover": cover,
        "wrapper": allWrappers[index]
      });
    });
  }

  // inline arrow function, direct return
  function getWrapper(cover) {
    const index = players.findIndex(
      (player) => player.cover === cover
    );
    return players[index].wrapper;
  }

  function resetBackground(backgroundSelector) {
    const allBackgrounds = document.querySelectorAll(backgroundSelector);

    function showBackground(background) {
      background.classList.remove("bg1");
    }
    allBackgrounds.forEach(showBackground);
  }

  function resetCurtains(curtainSelector) {
    const allCurtains = document.querySelectorAll(curtainSelector);

    function showCurtain(curtain) {
      curtain.classList.remove("active");
    }
    allCurtains.forEach(showCurtain);
  }

  function showAllButtons(buttonSelector) {
    const allButtons = document.querySelectorAll(buttonSelector);

    function showButton(button) {
      button.classList.remove("hide");
    }
    allButtons.forEach(showButton);
  }

  function resetButtons(buttonSelector) {
    const allButtons = document.querySelectorAll(buttonSelector);

    function showButton(button) {
      button.classList.remove("isOpen");
    }
    allButtons.forEach(showButton);
  }

  function animationEndHandler(evt) {

    const animationName = evt.animationName;
    console.log(animationName);

    if (animationName === "fadingOut") {
      fadeReset();
    }
  }

  function fadeReset() {
    body.classList.remove("fadingOut");
    resetBackground("body");
    resetCurtains(".with-curtain");
    showAllButtons(".hide");
    resetButtons(".outer");
  }

  function resetPage() {
    body.classList.add("fadingOut");
  }

  function exitClickHandler() {
    resetPage();
  }

Someone has given you a working example using data attributes in your first post.

I believe that’s a full working example.

That is the best one from the list of examples.

That is what you are saying.

From this post here where I first posted it: Making note*

In reference to this line here which only allowed 1 video to be played:

Which can be seen here: https://jsfiddle.net/1o2nr8mx/

const container = cover.previousElementSibling.querySelector(".container");

Changing it to this allows for more than 1 video to be played.

Use the button index as a reference to select which container with the same index, should be shown. https://jsfiddle.net/ypconqxb/

const buttonIndex = Array.from(cover.children).findIndex(ele => ele === playButton); //zero based index
const container = cover.previousElementSibling.querySelector(".container.play" + (buttonIndex + 1));