Skip to content Skip to sidebar Skip to footer

In Javascript, How Do I Write One Function That Will Effect Multiple Buttons, Each With Their Own Separate Arrays?

I am currently setting up an application with three separate buttons, each which is supposed to randomly select an element from an array specific to that button. I've successfully

Solution 1:

var urlsByColor = {
  grey: ['Statblocks/Grey/badger.png', 'Statblocks/Grey/giantrat.png', 'Statblocks/Grey/badger.png', 'Statblocks/Grey/boar.png', 'Statblocks/Grey/panther.png', 'Statblocks/Grey/gitant badger.png', 'Statblocks/Grey/dire wolf.png', 'Statblocks/Grey/giant elk.png'],
  rust: ['Statblocks/Rust/rat.png', 'Statblocks/Rust/owl.png', 'Statblocks/Rust/mastiff.png', 'Statblocks/Rust/goat.png', 'Statblocks/Rust/giant goat.png', 'Statblocks/Rust/giant boar.png', 'Statblocks/Rust/lion.png', 'Statblocks/Rust/brown bear.png'],
  tan: ['Statblocks/Tan/jackal.png', 'Statblocks/Tan/ape.png', 'Statblocks/Tan/baboon.png', 'Statblocks/Tan/axe beak.png', 'Statblocks/Tan/black bear.png', 'Statblocks/Tan/giant weasel.png', 'Statblocks/Tan/giant hyena.png', 'Statblocks/Tan/tiger.png']
};

functionchangeBall (e) {
  const urls = urlsByColor[e.target.id];
  const randomIndex = Math.floor(Math.random() * urls.length);
  const randomUrl = urls[randomIndex];
  const associatedBall = e.target.closest('.column').querySelector('.ball');
  
  console.log(`change ${e.target.id} to ${randomUrl}`);
  associatedBall.src = randomUrl;
  console.log(associatedBall);
}

[...document.querySelectorAll('.color.button')].forEach(button =>
  button.addEventListener('click', changeBall)
);
<divclass="row"><divclass="column"><h1>Grey Bag of Tricks</h1><buttonclass="button color"id='grey'>Draw from the Bag</button><imgsrc=""alt=""class="ball"id="greyBall"></div><divclass="column"><h1>Rust Bag of Tricks</h1><buttonclass="button color"id='rust'>Draw from the Bag</button><imgsrc=""alt=""class="ball"id="rustBall"></div><divclass="column"><h1>Tan Bag of Tricks</h1><buttonclass="button color"id='tan'>Draw from the Bag</button><imgsrc=""alt=""class="ball"id="tanBall"></div></div>

Ok so a few things changed.

  • I extracted the lists of urls out to a map, that can use the id of the button to lookup what urls to use
  • The markup for the buttons and balls changed to have common classes for finds and lookups
  • When a click happens, we get the urls by the button id
  • We then get a random index like was done before
  • We get the random assoicated url for that index
  • We find the ball that is associated with the button contextually by the parent column class
  • And finally we change the ball's url

Solution 2:

In general, you put the similar repeated part in a function, then you abstract all the parts that are different in each usage by replacing them with parameters to the function:

functionaddHandler(buttonSelector, ballImages, ballId) {
  const button = document.querySelector(buttonSelector);
  button.addEventListener('click', e => {
    for (let i=0; i<ballImages.length; i++) {
      let ball = ballImages[Math.floor(Math.random() * ballImages.length)];
      document.getElementById(ballId).src = ball;
    }
  });
}

so that you can call it like

addHandler('#grey', ['Statblocks/Grey/badger.png', 'Statblocks/Grey/giantrat.png', 'Statblocks/Grey/badger.png', 'Statblocks/Grey/boar.png', 'Statblocks/Grey/panther.png', 'Statblocks/Grey/gitant badger.png', 'Statblocks/Grey/dire wolf.png', 'Statblocks/Grey/giant elk.png'], 'greyBall');
addHandler('#rust', ['Statblocks/Rust/rat.png', 'Statblocks/Rust/owl.png', 'Statblocks/Rust/mastiff.png', 'Statblocks/Rust/goat.png', 'Statblocks/Rust/giant goat.png', 'Statblocks/Rust/giant boar.png', 'Statblocks/Rust/lion.png', 'Statblocks/Rust/brown bear.png'], 'rustBall');
addHandler('#tan', ['Statblocks/Tan/jackal.png', 'Statblocks/Tan/ape.png', 'Statblocks/Tan/baboon.png', 'Statblocks/Tan/axe beak.png', 'Statblocks/Tan/black bear.png', 'Statblocks/Tan/giant weasel.png', 'Statblocks/Tan/giant hyena.png', 'Statblocks/Tan/tiger.png'], 'tanBall');

That's still some duplication though. We can avoid that by doing extra processing on the data so that we can pass simpler values:

functionaddHandler(id, imageNames) {
  const buttonSelector = '#' + id;
  const ballImages = imageNames.map(name =>`Statblocks/${id[0].toUpperCase()+id.slice(1)}/${name}.png}`);
  const ballId = id + 'Ball';

  … // rest as before
}

addHandler('grey', ['badger', 'giantrat', 'badger', 'boar', 'panther', 'gitant badger', 'dire wolf', 'giant elk']);
addHandler('rust', ['rat', 'owl', 'mastiff', 'goat', 'giant goat', 'giant boar', 'lion', 'brown bear']);
addHandler('tan', ['jackal', 'ape', 'baboon', 'axe beak', 'black bear', 'giant weasel', 'giant hyena', 'tiger']);

Last I'd change the querySelector to a getElementById lookup, and remove the unnecessary loop, and we arrive at

functionaddHandler(id, imageNames) {
  const ballImages = imageNames.map(name =>`Statblocks/${id[0].toUpperCase()+id.slice(1)}/${name}.png}`);
  const button = document.getElementById(id);
  button.addEventListener('click', e => {
    let ball = ballImages[Math.floor(Math.random() * ballImages.length)];
    document.getElementById(id + 'Ball').src = ball;
  });
}

addHandler('grey', ['badger', 'giantrat', 'badger', 'boar', 'panther', 'gitant badger', 'dire wolf', 'giant elk']);
addHandler('rust', ['rat', 'owl', 'mastiff', 'goat', 'giant goat', 'giant boar', 'lion', 'brown bear']);
addHandler('tan', ['jackal', 'ape', 'baboon', 'axe beak', 'black bear', 'giant weasel', 'giant hyena', 'tiger']);

Post a Comment for "In Javascript, How Do I Write One Function That Will Effect Multiple Buttons, Each With Their Own Separate Arrays?"