using the .each() function doesn't allow me to use the .on() function

178 Views Asked by At

I would like to get some guidance on the use of .each() and .on().

I am trying to remove the class playing from the element that contains them after they have ended their transition. However, on entering the code below, the console spews this out :

jquery.min.js:2 Uncaught TypeError: key.on is not a function

My code:

$(document).ready(function(){
 function removeTransition(e) {
      if (e.propertyName !== 'transform') return;
      e.target.classList.remove('playing');
  }


  function playSound(e){
    const audio = $(`audio[data-key="${e.keyCode}"]`);
    const key = $(`div[data-key="${e.keyCode}"]`);
    key.addClass('playing');
    if (!audio) return;
    audio[0].currentTime = 0;
    audio[0].play();
  }
      const keys = $("[class=key]");
    keys.each(key => key.on('transitionend', removeTransition));
    $('body').on("keydown", playSound);

})

HTML:

<!DOCTYPE HTML>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>JS Drum Kit</title>
        <link rel="stylesheet" href="styles.css">
    </head> 
    <body>
        <div class="keys">
            <div data-key="65" class="key">
                <kbd>A</kbd>
                <span class="sound">clap</span>
            </div>
            <div data-key="83" class="key">
                <kbd>S</kbd>
                <span class="sound">hihat</span>
            </div>
            <div data-key="68" class="key">
                <kbd>D</kbd>
                <span class="sound">kick</span>
            </div>
            <div data-key="70" class="key">
                <kbd>F</kbd>
                <span class="sound">openhat</span>
            </div>
            <div data-key="71" class="key">
                <kbd>G</kbd>
                <span class="sound">boom</span>
            </div>
            <div data-key="72" class="key">
                <kbd>H</kbd>
                <span class="sound">ride</span>
            </div>
            <div data-key="74" class="key">
                <kbd>J</kbd>
                <span class="sound">snare</span>
            </div>
            <div data-key="75" class="key">
                <kbd>K</kbd>
                <span class="sound">tom</span>
            </div>
            <div data-key="76" class="key">
                <kbd>L</kbd>
                <span class="sound">tink</span>
            </div>

        </div>

        <audio data-key="65" src="sounds/clap.wav"></audio>
        <audio data-key="83" src="sounds/hihat.wav"></audio>
        <audio data-key="68" src="sounds/kick.wav"></audio>
        <audio data-key="70" src="sounds/openhat.wav"></audio>
        <audio data-key="71" src="sounds/boom.wav"></audio>
        <audio data-key="72" src="sounds/ride.wav"></audio>
        <audio data-key="74" src="sounds/snare.wav"></audio>
        <audio data-key="75" src="sounds/tom.wav"></audio>
        <audio data-key="76" src="sounds/tink.wav"></audio>

    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
    <script src="script.js"></script>

    </body>
</html>

CSS:

html{
    font-size: 12px;
    background:url(http://cdn.wallpapersafari.com/95/30/yYO1JG.jpg);
    background-size: cover;
    font-family: 'Nunito Sans', sans-serif;
}

.keys{
    display: flex;
    flex: 1;
    min-height:100vh;
    align-items: center;
    justify-content: center;
}

.key{
    border: 3px solid black;
    border-radius:5px;
    margin:1rem;
    font-size: 1.5rem;
    padding:1rem .5rem;
    width:100px;
    text-align: center;
    color:black;
}

.playing {
    transform:scale(1.1);
    border-color:#9C2A00;
}
4

There are 4 best solutions below

7
Juha Tauriainen On

Key on this case is the element, not jQuery object. You need wrap it with jQuery object, like this.

keys.each((index, key) => $(key).on('transitionend', removeTransition));
4
Hitmands On

function TestCtrl($) {
  $btns = $('.test');
  
  
  // the callback will be invoked with index and node
  $btns.each((index, node) => {
    $(node).click(() => console.log(index, node));
  })
}


jQuery(document).ready(TestCtrl);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<button class="test">Test</button>
<button class="test">Test</button>
<button class="test">Test</button>
<button class="test">Test</button>
<button class="test">Test</button>
<button class="test">Test</button>

Edit

Just wanna meet your code-case:

function TestCtrl($) {
  let test = $('#Test');
  let $keys = $('.key');
  
  test.click(() => $keys.toggleClass('testing'));
  
  function onKeyTransitionEnd(event, $key, nodeIndex) {
    console.log("transitionend", nodeIndex);
  }
  
  $keys.each((i, node) => {
    let $node = $(node)
    .on(
      'transitionend', 
      e => onKeyTransitionEnd(e, $node, i)
    ); 
  });
}


jQuery(document).ready(TestCtrl);
.keys {
  display: flex;
  flex-wrap: wrap;
}
.key {
  width: 100px;
  height: 50px;
  margin: 5px;
  background: yellow;
  transition: 500ms all linear;
}

.key.testing {
  background: cyan;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="Test">Test It</button>
<hr />

<div class="keys">
  <div class="key"></div>
  <div class="key"></div>
  <div class="key"></div>
  <div class="key"></div>
  <div class="key"></div>
  <div class="key"></div>
  <div class="key"></div>
  <div class="key"></div>  
</div>

0
Alvaro On

There are some things you need to change:

• The way you are assigning the each function was not working correctly

keys.each(function(){$(this).on('transitionend', removeTransition)});

• The reference to the property from the transition was not correct

if (e.originalEvent.propertyName !== 'transform') return;

• The way you were assigning the key value was creating some conflicts

const audio = $('audio[data-key="' + e.keyCode + '"]');
const key = $('div[data-key="' + e.keyCode + '"]');

• The most important, probably, there was no transition in the CSS so there was no callback on transitionend

transition: transform .5s ease;

JSFiddle

$(document).ready(function() {
  function removeTransition(e) {
    if (e.originalEvent.propertyName !== 'transform') return;
    e.target.classList.remove('playing');
  }

  function playSound(e) {
    const audio = $('audio[data-key="' + e.keyCode + '"]');
    const key = $('div[data-key="' + e.keyCode + '"]');
    key.addClass('playing');
    if (!audio) return;
    audio[0].currentTime = 0;
    audio[0].play();
  }
  const keys = $("[class=key]");
  keys.each(function(){$(this).on('transitionend', removeTransition)});
  $('body').on("keydown", playSound);

})
html {
  font-size: 12px;
  background: url(http://cdn.wallpapersafari.com/95/30/yYO1JG.jpg);
  background-size: cover;
  font-family: 'Nunito Sans', sans-serif;
}

.keys {
  display: flex;
  flex: 1;
  min-height: 100vh;
  align-items: center;
  justify-content: center;
}

.key {
  border: 3px solid black;
  border-radius: 5px;
  margin: 1rem;
  font-size: 1.5rem;
  padding: 1rem .5rem;
  width: 100px;
  text-align: center;
  color: black;
}

.playing {
  transform: scale(1.1);
  border-color: #9C2A00;
  transition: transform .5s ease;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="keys">
  <div data-key="65" class="key">
    <kbd>A</kbd>
    <span class="sound">clap</span>
  </div>
  <div data-key="83" class="key">
    <kbd>S</kbd>
    <span class="sound">hihat</span>
  </div>
  <div data-key="68" class="key">
    <kbd>D</kbd>
    <span class="sound">kick</span>
  </div>
  <div data-key="70" class="key">
    <kbd>F</kbd>
    <span class="sound">openhat</span>
  </div>
  <div data-key="71" class="key">
    <kbd>G</kbd>
    <span class="sound">boom</span>
  </div>
  <div data-key="72" class="key">
    <kbd>H</kbd>
    <span class="sound">ride</span>
  </div>
  <div data-key="74" class="key">
    <kbd>J</kbd>
    <span class="sound">snare</span>
  </div>
  <div data-key="75" class="key">
    <kbd>K</kbd>
    <span class="sound">tom</span>
  </div>
  <div data-key="76" class="key">
    <kbd>L</kbd>
    <span class="sound">tink</span>
  </div>

</div>

<audio data-key="65" src="sounds/clap.wav"></audio>
<audio data-key="83" src="sounds/hihat.wav"></audio>
<audio data-key="68" src="sounds/kick.wav"></audio>
<audio data-key="70" src="sounds/openhat.wav"></audio>
<audio data-key="71" src="sounds/boom.wav"></audio>
<audio data-key="72" src="sounds/ride.wav"></audio>
<audio data-key="74" src="sounds/snare.wav"></audio>
<audio data-key="75" src="sounds/tom.wav"></audio>
<audio data-key="76" src="sounds/tink.wav"></audio>

3
vijay On

Try this , use addEventListener instead of on

keys.each(function(key) 
    {
    keys[key].addEventListener("transitionend", removeTransition);
    });

html {
  font-size: 12px;
  background: url(http://cdn.wallpapersafari.com/95/30/yYO1JG.jpg);
  background-size: cover;
  font-family: 'Nunito Sans', sans-serif;
}
.keys {
  display: flex;
  flex: 1;
  min-height: 100vh;
  align-items: center;
  justify-content: center;
}
.key {
  border: 3px solid black;
  border-radius: 5px;
  margin: 1rem;
  font-size: 1.5rem;
  padding: 1rem .5rem;
  width: 100px;
  text-align: center;
  color: black;
}
.playing {
  transform: scale(1.1);
  border-color: #9C2A00;
  transition: transform 1s ease;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<!DOCTYPE HTML>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>JS Drum Kit</title>
  <link rel="stylesheet" href="styles.css">
</head>

<body>
  <div class="keys">
    <div data-key="65" class="key">
      <kbd>A</kbd>
      <span class="sound">clap</span>
    </div>
    <div data-key="83" class="key">
      <kbd>S</kbd>
      <span class="sound">hihat</span>
    </div>
    <div data-key="68" class="key">
      <kbd>D</kbd>
      <span class="sound">kick</span>
    </div>
    <div data-key="70" class="key">
      <kbd>F</kbd>
      <span class="sound">openhat</span>
    </div>
    <div data-key="71" class="key">
      <kbd>G</kbd>
      <span class="sound">boom</span>
    </div>
    <div data-key="72" class="key">
      <kbd>H</kbd>
      <span class="sound">ride</span>
    </div>
    <div data-key="74" class="key">
      <kbd>J</kbd>
      <span class="sound">snare</span>
    </div>
    <div data-key="75" class="key">
      <kbd>K</kbd>
      <span class="sound">tom</span>
    </div>
    <div data-key="76" class="key">
      <kbd>L</kbd>
      <span class="sound">tink</span>
    </div>

  </div>

  <audio data-key='65' src="sounds/clap.wav"></audio>
  <audio data-key="83" src="sounds/hihat.wav"></audio>
  <audio data-key="68" src="sounds/kick.wav"></audio>
  <audio data-key="70" src="sounds/openhat.wav"></audio>
  <audio data-key="71" src="sounds/boom.wav"></audio>
  <audio data-key="72" src="sounds/ride.wav"></audio>
  <audio data-key="74" src="sounds/snare.wav"></audio>
  <audio data-key="75" src="sounds/tom.wav"></audio>
  <audio data-key="76" src="sounds/tink.wav"></audio>


</body>

</html>
<script>
  $(document).ready(function() {
    function removeTransition(e) {
      if (e.propertyName !== 'transform') return;
      e.target.classList.remove('playing');
    }


    var key;

    function playSound(e) {
      const audio = $(`audio[data-key="${e.keyCode}"]`);
      const key = $(`div[data-key="${e.keyCode}"]`);
      key.addClass('playing');
      if (audio.length == 0) return;
      audio[0].currentTime = 0;
      audio[0].play();
    }
    $('body').on("keydown", playSound);
    const keys = $("[class=key]");
    keys.each(function(key) {
      keys[key].addEventListener("transitionend", removeTransition);
    });


  });
</script>