Marwan Ansari
Marwan Ansari

Reputation: 123

Using Jquery's .each() function makes the web page lag on mobile devices

When I click on a span or div element the audio is played. The function also contains .each() to prevent the audios from overlapping each other, that is, when a user clicks a word, the audio is played and when a user clicks on another word, the current audio stops playing and the new audio is played.

The problem is, I have over a thousand span or div elements on some of my pages and while the web page works perfectly on computers, it lags horribly on mobile/smaller devices.

The web pages takes a maximum of 2 seconds to load on computers but takes 15-20 seconds on mobile/smaller devices. I've already found the culprit to be this .each() function because I guess it loops through the whole 1000+ elements on the page when it loads and the mobile devices are unable to take this load.

I'm looking for a solution to this problem because if I remove the .each() function, the page loads quickly on mobile devices but then the audio overlapping issue comes up. Any help would be appreciated.

<span data-audio-url="hello.mp3">Hello</span>
<span data-audio-url="hello.mp3">Hello</span>
<span data-audio-url="hello.mp3">Hello</span>    
<div data-audio-url="hello.mp3">Hello</div>
$("span, div").each(function(event) { 
  $(this).data('audio-object', new Audio()); 
}).on('click', function (event, e) {
  var e = event || window.event;
  e.cancelBubble = true;

  if (e.stopPropagation) 
    e.stopPropagation();

  var audio_url = $(this).attr('data-audio-url');

  $("span, div").each(function() {
    var audio = $(this).data('audio-object');
    if (audio.src) {
      audio.pause();
      audio.currentTime = 0;
    }    
  });

  var clickedAudio = $(this).data('audio-object');
  if (!clickedAudio.src) { 
    clickedAudio.src = audio_url; 
  }    
  clickedAudio.play();
});

Upvotes: 1

Views: 110

Answers (1)

Rory McCrossan
Rory McCrossan

Reputation: 337637

The problem is because you first needlessly loop through every selected element and then add an audio element in to jQuery's data cache for that element. Then within the click handler itself you again loop through all the elements and pause the audio elements in their data. These two loops are very costly in terms of performance.

To simplify this just use a single audio element in the DOM when the page loads and change its src when the relevant element is clicked. Using this method you don't need either of the loops.

As suggested in the comments by @LieRyan, you could then avoid the implicit loop through the elements to bind all the event handlers by using a single delegated event handler on the container of all the span and div elements. Try this:

var audio = $('#player')[0];

$('#container').on('click', 'span, div', function(e) {
  e.stopPropagation();
  var audio_url = $(this).data('audio-url');
  audio.src = audio_url;
  audio.play();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<div id="container">
  <span data-audio-url="http://media.w3.org/2010/07/bunny/04-Death_Becomes_Fur.oga">Hello</span>
  <span data-audio-url="http://media.w3.org/2010/05/sound/sound_90.mp3">Hello</span>
  <div data-audio-url="http://media.w3.org/2010/11/rrs006.oga">Hello</div>
</div>

<audio id="player"></audio>

Note that preventDefault() isn't needed here, as span and div elements have no default click behaviour to be prevented.

Also note that having thousands of elements listing samples sounds like overkill. I'd strongly suggest you implement paging, searching and/or filtering to make it much easier for your users to navigate.

Upvotes: 6

Related Questions