Home > Software engineering >  Javascript Help me make this DRY
Javascript Help me make this DRY

Time:04-03

I want to make this code fit the DRY principle, I see that I repeat myself, but there must be a way to shorten this code into something less WET. The goal is to make a function that plays a sound when hovering over a card, it works, but as of now the code is WET.

<header  id="play_1">
  <audio>
    <source src="/voices/sound1"></source>
  </audio>
</header>


<header  id="play_2">
  <audio>
    <source src="/voices/sound2"></source>
  </audio>
</header>

<header  id="play_3">
  <audio>
    <source src="/voices/sound3"></source>
  </audio>
</header>
window.onload=function(){

var playHover = document.getElementById('play_1'),
    audios = document.querySelectorAll('audio');
console.log(audios);


playHover.addEventListener('mouseover', function() {
[].forEach.call(audios, function(audio) {
  audio.play();
});
}, false);

var playHover2 = document.getElementById('play_2'),
    audios = document.querySelectorAll('audio');
console.log(audios);


playHover2.addEventListener('mouseover', function() {
[].forEach.call(audios, function(audio) {
  audio.play();
});
}, false);

var playHover3 = document.getElementById('play_3'),
    audios = document.querySelectorAll('audio');
console.log(audios);


playHover3.addEventListener('mouseover', function() {
[].forEach.call(audios, function(audio) {
  audio.play();
});
}, false);
}

I tried making a for loop to put in the id's number, but I failed to make it work I inspired the for loop mostly from someone else having a similar dry problem here

window.onload=function(){

for ( var i = 1; i < 4; i   ) window.onload();
var playHover = document.getElementById('play_'   i),
    audios = document.querySelectorAll('audio');
console.log(audios);


playHover.addEventListener('mouseover', function() {
[].forEach.call(audios, function(audio) {
  audio.play();
});
}, false);
}

CodePudding user response:

Skip declaring it as a variable:

for (let i = 1; i < 3; i  ) {

document.getElementById('play_   ' i),
    audios = document.querySelectorAll('audio').addEventListener('mouseover', function() {
[].forEach.call(document.querySelectorAll('audio'), function(audio) {
  audio.play();
});
}, false)
}

CodePudding user response:

Unfortunately, because I don't have any obvious access to an audio-streaming service (that allows their content to be streamed freely) I can't offer you much of a demo.

However the I'd suggest the following revisions to your code, based on the following observations:

  • the DOM of each element you're working with has the same structure as all the others,
  • you're effectively doing the same thing in each instance,
  • it's not necessary to bind an event-handler using the id property, and
  • variables can be instantiated inside of functions as they're needed.

With that in mind I suggest the following JavaScript (explanatory comments are in the code):

// here we're using an alternative EventTarget.addEventListener() to bind the
// event-handling once the window, and contents, have loaded and are ready:

// the empty parentheses are used because we're not passing any arguments to
// the enclosed function; using EventTarget.addEventListener() makes the
// Event Object available automatically, but we don't need to use that
// in this instance:
window.addEventListener('DOMContentLoaded', () => {

  // defining the named function to be bound as the event-handler; in this
  // function we pass in a reference to the Event Object passed by
  // EventTarget.addEventListener():
  const playAudio = (evt) => {
    // from the Event Object we retrieve the <header> element to which
    // the function was bound, and triggered:
    let currentHeader = evt.currentTarget,
      // from the <header> we use Element.querySelector() to find
      // the first (if any) <audio> element descendant:
      currentAudio = currentHeader.querySelector('audio'),
      // finding all <audio> elements in the document (since the way
      // I read your code it seemed like you were playing all <audio>
      // elements, but I imagine I was wrong:
      allAudios = document.querySelectorAll('audio');

    // if you want to play just the <audio> element from the <header>
    // that your user is hovering over:
    currentAudio.play();
    // if, instead, you do want to play all <audios> then we can
    // iterate over the NodeList of <audio> elements using
    // NodeList.prototype.forEach():
    allAudios.forEach(
      // passing in a reference the current <audio> element, and
      // calling its play() method:
      (audioElement) => audioElement.play()
    );
  };

  // using document.querySelectorAll() to retrieve all <header> elements:
  document.querySelectorAll('header')
    // using NodeList.prototype.forEach() to iterate over that NodeList:
    .forEach(
      // passing a reference to the current <header> of the NodeList to
      // the Arrow function; and within the function binding the
      // playAudio() function (note the deliberate lack of parentheses)
      // as the event-handler for the 'mouseover' event:
      (headerElement) => headerElement.addEventListener('mouseover', playAudio))
});
<header  id="play_1">
  <audio>
    <source src="/voices/sound1">
  </audio>
</header>

<header  id="play_2">
  <audio>
    <source src="/voices/sound2">
  </audio>
</header>

<header  id="play_3">
  <audio>
    <source src="/voices/sound3">
  </audio>
</header>

References:

  • Related