Events do not work with dynamic video?

1

I'm using a page where content is loaded dynamically with ajax, the problem is that some events, especially video, are not working as they should, for example:

My functions.js loaded when entering the site:

$(document).on("timeupdate", "#video", function(){
    console.log('video playing');
});

And then when the user clicks to watch a video I add the HTML of it into a div and show it:

$(div).html(player_html);

The video plays, but events like the above are not being triggered, I'm avoiding having to load the script with the events by the time I add the video, how can I solve this problem?

The example below seems to work fine, but it does not look like an "elegant" solution to use:

document.addEventListener('timeupdate', function(e){
    if(e.target.id == "video"){
        console.log('video playing');
    }
}, true);

But there is another question, if the above example worked, why does not it work with jquery?

    
asked by anonymous 04.05.2017 / 14:23

2 answers

4

The problem is that the timeupdate event is not a bubble event . This means that you can not treat it through the parent element, as done in:

$(document).on("timeupdate", "#video", function(){
    console.log('video playing');
});

This happens because the event is, in this case, associated with document and not with #video ¹ element. Making direct event delegation with pure JavaScript is possible in the way you did:

document.addEventListener('timeupdate', function(e){
    if(e.target.id == "video"){
        console.log('video playing');
    }
}, true);

The two forms not (possibly not always) are analogous.

That is, the only way (except for the above) is to associate the event directly with the #video element. You can do this the moment you enter it in the DOM, as explained by Caio, in your response , in the example 2.

  

Example 3 of the Caio worked as presented because the click event is a bubble event .

$(() => {

  const player = '<video id="video" width="400" controls><source src="https://www.w3schools.com/html/mov_bbb.mp4"type="video/mp4"><source src="https://www.w3schools.com/html/mov_bbb.ogg"type="video/ogg"></video>';

  $("button").on("click", event => {
    $("#container").html(player);

    $("#video").on("timeupdate", event => {
      console.log("Video playing...");
    });
  });

});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script><divid="container"></div>
<button>Adicionar</button>

References

JavaScript | Events bubbles

Javascript - Event order

Why do not audio and video events bubble?

Understanding Event Delegation

Notes

(1) : According to the Understanding Event Delegation page on the official jQuery website, when done something like:

$( "#list" ).on( "click", "a", function( event ) {
    event.preventDefault();
    console.log( $( this ).text() );
});

The click event is delegated to the #list element and, when treated, it is checked whether the event target matches the selector passed in the second parameter. Reading:

  

According to the summary on the page:

  

Event-related to the process of using event propagation (bubbling) to handle events at a higher level in the DOM than the element on which the event originated. It allows us to attach to a single event listener for elements that exist now in the future.

The on function actually makes use of the bubbling effect, or event propagation, for these cases, so for an event that does not propagate, it will not work.

(2) : Apparently the two forms are analogous when the event in question propagates through the DOM, since the event triggered on the target element will also be triggered on its predecessor elements, also in the document element. Using the third parameter in addEventListener , capture , as true causes the event in question to always be triggered also in the element in question, so the event delegation form with pure JavaScript works as expected. In this case, doing something like the example below is also valid:

const container = document.getElementById("container");

container.addEventListener('timeupdate', function(e) {
    if(e.target.id == "video"){
        console.log('video playing');
    }
}, true);

I have not yet found any documentation that tells you if the on function treats all events as bubble or whether there is a different behavior for events that are not. In note 1, it is said that the on event actually uses the bubbling effect of the event to work, but it is not conclusive if it does this regardless of the event.

    
08.05.2017 / 00:50
2

Your issue is known, and it's called event delegation . See, you said yourself that you create the elements dynamically. This means that by the time you declare the event routine, some elements may not yet exist. Here's an example:

Example 1

//evento para adicionar boxes "dinamicamente"
$('button').on('click', function(){
  var _el = $('<div class="box"></div>');

  $('.container').append(_el);
});

//evento do box, equivalente ao seu evento do video
$('.box').on('click', function(){
  alert('box clicked');
});
.box{
  width: 50px;
  height: 50px;
  background-color: green;
  display: inline-block;
  margin: 10px;
}

.container{
  padding: 1em;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script><button>Adicionarbox</button><divclass="container">
  <div class="box"></div>
</div>

Note that if you click the first bound , it already exists at the moment the method is bound , you receive an alert. If you create more boxes and click on them, the alert does not occur: Why? In a simplified way, precisely because of the fact that, at the time of the declaration, these new elements did not exist.

You could correct this by declaring the event along with the element's creation. Here's the next example:

Example 2

//evento para adicionar boxes "dinamicamente"
$('button').on('click', function(){
  
  //criação do evento junto com o elemento
  var _el = $( "<div/>", {
    "class": "box",
    click: function() {
      alert('box clicked');
    }
  });
  
  $('.container').append(_el);
});

//evento do box, equivalente ao seu evento do video
$('.box').on('click', function(){
  alert('box clicked');
});
.box{
  width: 50px;
  height: 50px;
  background-color: green;
  display: inline-block;
  margin: 10px;
}

.container{
  padding: 1em;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script><button>Adicionarbox</button><divclass="container">
  <div class="box"></div>
</div>

Notice that the new boxes now run the alert when clicked. But there is a setback: I had to declare the event twice - once for the existing boxes , and the other for the boxes at creation time (try removing the second statement to see what happens).

There is a third way, which I believe to be the best, which is precisely the delegation of events. You can bind (bind ) the event of an element to the parent of this element, making only the existence of the parent, rather than the elements themselves, mandatory. See the third example:

Example 3:

//evento para adicionar boxes "dinamicamente"
$('button').on('click', function(){
  
  var _el = $('<div class="box"></div>');
  
  $('.container').append(_el);
  
});

//evento do box, equivalente ao seu evento do video
$('.container').on('click', '.box', function(){
  alert('box clicked');
});
.box{
  width: 50px;
  height: 50px;
  background-color: green;
  display: inline-block;
  margin: 10px;
}

.container{
  padding: 1em;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script><button>Adicionarbox</button><divclass="container">
  <div class="box"></div>
</div>

Now I assign the event to the container (that is, the parent, which I'm sure exists) and, as an additional parameter, I gave the .box child, which is where I I want to effectively link my event. Notice that in this way, the action occurs for both preexisting and new elements, as long as they are .box (in their case, something like .video ). The only requirement is that you use a parent element ( .container ) that sure will exist at the time the code is executed. If not, you resort to the problem.

Points of Attention:

  • .bind() is deprecated. It is recommended to use .on() (which is what allows you to do the delegation).

  • >
  • If you are dynamically building elements #video , use a class ( .video ), since IDs should always be unique on the page.

  • 04.05.2017 / 15:16