Don’t events work with dynamic video?

Asked

Viewed 161 times

2

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

Man 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 his HTML to a div and show the same:

$(div).html(player_html);

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

The example below seems to work well, but it does not seem to me a "stylish" solution to use:

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

But then another question arises, if the above example worked, why doesn’t it work with jquery?

2 answers

5


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

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

This is because the event is, in this case, associated with document and not to the element #video¹. Making the event delegation direct, 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);

Both forms nay (possibly not always) are analogs².

That is, the only way (except the above) is to associate the event directly to the element #video. You can do this at the same time you insert it into the DOM, as explained by Caio, in his reply, in example 2.

Caio’s example 3 worked the way presented because the event click 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>

<div id="container"></div>
<button>Adicionar</button>

References

Javascript | Events & Events

Javascript - Event order

Why don’t audio and video Events Bubble?

Understanding Event Delegation


Notes

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

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

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

This Second, selector Parameter Tells the Handler to Listen for the specified Event, and when it Hears it, check to see if the Triggering element for that Event Matches the Second Parameter.

According to the summary on the page:

Event delegation refers 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 a single Event Listener for Elements that exist now or in the Future.

The function on really makes use of the effect Bubbling, or propagation of the event, for such cases, therefore, for an event that does not propagate, will not work.

(2): Apparently the two forms are analogous when the event in question is propagated by the DOM, because the event fired at the target element will also be fired at its predecessor elements, being fired, therefore, also at the element document. Use the third parameter in addEventListener, capture, as true makes the event in question always be fired also in the element in question, so the form of delegation of event 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 haven’t found any documentation that speaks if the function on treat all events as bubble or if there is a differentiated behavior for events that are not. In note 1, it is said that the event on uses, in fact, the effect Bubbling of the event to work, but it is not conclusive whether it does so regardless of the event.

  • So I was wrong, the . on does not work as Mutationobserver, the event is added to the father and not to the new element :/

  • When the element is removed, events will remain active?

  • @Leoletto if the event is associated with the element, if it is removed, so will the event. I’m not sure I understand your question correctly.

  • Then I will check this so that I can formulate a better question if it is the case, I think I will have to opt for pure javascript even, is the fastest and feasible solution for me at the moment

  • Better than assigning the event when the video is inserted into the DOM? Are you sure?

  • As far as I know, those two forms are rather analogous. Strange the pure js version has worked, since this event does not bubble...

  • @bfavaretto Worked because of the last parameter useCapture by the way. In this case the event is triggered first to document before being shot at the element itself, no? As long as the codes are analogous, I believe they are not for all events. In the function jQuery.event.add used by jQuery.on has a comment like this: "Only use addeventlistener if the special Events Handler Returns false" (approx. line 4600, jQuery 2.2.4, uncompress). I still do not understand exactly what happens, but I imagine that the behavior changes according to the event treated.

  • Ah, I hadn’t noticed that that code used capture. It makes sense that it would work in this case. And I will look at the jQuery code to try to understand when it uses addeventlistener and when it does not use.

  • Excellent. I had already seen something about the fact that Bubbling did not occur, but I confess that I did not go as deep as I wanted. Good answer

  • According to that Dude, the audio and video events don’t bubble up because it doesn’t make sense to them. It shows a way to wrap to use the delegation via jquery. I haven’t tried it, but it looks promising

  • @Caiofelipepereira I had seen this answer when I was researching, but I found the solution he presented unviable and I didn’t even mention it. It seemed too much code for little. In my view, assigning the event to the element when it is added to the DOM is much more practical and simple.

  • Since I am only doing an "upgrade" in the code, using pure js in the case would be the easiest option, because the same to be assigned when adding the elements would require a certain change in my current code, in this case I would prefer to start a new code of 0, foreseeing such events in the future.

  • @Andersoncarloswoss, as I said, did not test, hahaha. But so, curious that this has never been dealt with in the core of jquery

  • I think because, in fact, it doesn’t make much sense to do so. It’s particular events to the audio/video element, why would you treat them in a div. In the document, maybe, but I still think it’s unnecessary.

Show 9 more comments

3

Your problem is known, and it’s called delegation of events. You see, you said yourself you create the elements dynamically. This means that at the moment you declare the event routine, some elements may not yet exist. See this 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>Adicionar box</button>

<div class="container">
  <div class="box"></div>
</div>

Note that if you click on the first box box, which already exists at the time when the method is coupled (bound) at the click, you receive an alert. If you create more pits and click on them, the alert does not occur: Why? In a simplified way, precisely on account of the fact that, at the time of the declaration, these new elements did not exist.

You could correct this by declaring the event together with the element creation. See 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>Adicionar box</button>

<div class="container">
  <div class="box"></div>
</div>

See that now the new pits execute the alert when clicked. But there is a setback: I had to declare the event twice - one for the pits existing, and another for the new pits, at the time of creation (try removing the second statement to see what happens).

There is a third way, which I believe to be the best, which is precisely with the delegation of events. You can link (bind) the event of an element to the father of this element, making it obligatory only the existence of the father, and not of the elements themselves. 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>Adicionar box</button>

<div class="container">
  <div class="box"></div>
</div>

See that I now assign the event to container (that is, the father, which I am sure exists) and, as an additional parameter, informed the child .box, which is where I want to effectively tie my event. Notice that in this way, the action occurs both for elements that existed previously and for new, as long as they are of the type .box (in your case, something like .video). The only requirement is that you use a parent element (.container) that for sure will exist at the time of code execution. If not, you resort to the problem.

Points of attention:

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

  2. If you are dynamically creating elements #video, use a class (.video), since Ids should always be unique on the page.

  • According to example 3 if I declare $(document).on("timeupdate", "#video", function(){}) should work, but here it didn’t work, or I should replace the document by a page element?

  • I am aware of the use of ID for video, this is my only element with such ID

  • how you’re doing the content injection?

  • I have all the HTML for the video player saved in a JS variable, and then when the user clicks to watch something, I add this variable to div responsible for displaying dynamic content with $(".minha-div").html(player);

  • It all depends on how your HTML is inside that variable. timeupdateis also an event just for multimedia tags, so see if you’re assigning in the right place

  • I don’t understand, the HTML inside the div is normal, basically player = "<video id='#video' poster='' src=''></video> has more HTML being loaded together, but I don’t think it’s relevant here

  • The strange thing is that in the browser console the event is listed in my element, or am I wrong? http://imgur.com/a/C7VLn

  • No. It seems to be all right. I’m going to check it out here

  • Caio, I added in the question an example that worked and maybe help you understand the cause of the problem to help me.

  • 1

    I’ve seen something like it. Let me see if I can find

  • 1

    @Caiofelipepereira, me answered the question as well. See if you agree if you haven’t seen it yet. It may be of interest to you as well.

Show 6 more comments

Browser other questions tagged

You are not signed in. Login or sign up in order to post.