Why are videos played before inserted into DOM?

若如初见. 提交于 2019-12-11 01:09:34

问题


Why does executing something like the following

var videoBg;
videoBg = $('<video autoplay>');
videoBg.attr({
    'src': 'http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4',
    'height': 360,
    'width': 640
});

cause the video to be played before being inserted into the page's DOM?

Isn't the idea that the user should not be able to notice elements unless they are somewhere in the DOM?

What's the difference between an object being visible and an object being audible?


回答1:


Makes sense to me. It's being created you're just not rendering it. A workaround would be

var videoBg;
videoBg = $('<video>');
videoBg.attr({
    'src': 'http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4',
    'height': 360,
    'width': 640
});

$('body').append(videoBg);

videoBg.attr('autoplay', true)

EDIT Diving into things a bit further, here's a screenshot of a Chrome timeline:

Here's the call stack:

  • jQuery.extend.buildFragment @ jquery-2.1.1.js Line 5089
  • jQuery.parseHTML @ jquery-2.1.1.js Line 8810
  • jQuery.fn.init @ jquery-2.1.1.js Line 2735
  • jQuery @ jquery-2.1.1.js Line 76
  • (anonymous function) @ test.html Line 28
  • jQuery.Callbacks.fire @ jquery-2.1.1.js Line 3073
  • jQuery.Callbacks.self.fireWith @ jquery-2.1.1.js Line 3185
  • jQuery.extend.ready @ jquery-2.1.1.js Line 3391
  • completed @ jquery-2.1.1.js Line 3407

Of note:

(anonymous function) @ test.html Line 28 is:

videoBg = $('<video autoplay>');

and more important, jQuery.extend.buildFragment @ jquery-2.1.1.js Line 5089, is part of this block:

// Convert html into DOM nodes
} else {
  tmp = tmp || fragment.appendChild( context.createElement("div") );

  // Deserialize a standard representation
  tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase();
  wrap = wrapMap[ tag ] || wrapMap._default;
  tmp.innerHTML = wrap[ 1 ] + elem.replace( rxhtmlTag, "<$1></$2>" ) + wrap[ 2 ];

  // Descend through wrappers to the right content
  j = wrap[ 0 ];
  while ( j-- ) {
    tmp = tmp.lastChild;
  }

  // Support: QtWebKit
  // jQuery.merge because push.apply(_, arraylike) throws
  jQuery.merge( nodes, tmp.childNodes );

  // Remember the top-level container
  tmp = fragment.firstChild;

  // Fixes #12346
  // Support: Webkit, IE
  tmp.textContent = "";
}

Notice the line tmp = tmp || fragment.appendChild( context.createElement("div") );

Elsewhere it states var fragment = document.createDocumentFragment()

Leading us to documentation on createDocumentFragment(), which states:

DocumentFragments are DOM Nodes. They are never part of the main DOM tree. The usual use case is to create the document fragment, append elements to the document fragment and then append the document fragment to the DOM tree. In the DOM tree, the document fragment is replaced by all its children.

Since the document fragment is in memory and not part of the main DOM tree, appending children to it does not cause page reflow (computation of element's position and geometry). Consequently, using document fragments often results in better performance.




回答2:


Like audio, canvas, and others, <video> elements created in JavaScript are still passively functional even if they're not in the rendered DOM.

Doing

videoBg = $("<video>");

creates the element. It exists, it is functional. It is not yet a child of something in your page's DOM, though.

It simply doesn't have a connection in the DOM tree that would make it renderable.

Don't use autoplay if you don't want the video to start automatically playing. Instead, programmatically start the playback:

var videoBg;
videoBg = $("<video>");
videoBg.attr({
    src: "http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4",
    height: 360,
    width: 640
});
// When you're ready...
videoBg[0].load(); // Start loading `src`
videoBg[0].play(); // And play

Elaboration (edit)

I noticed you added to your question:

What's the difference between an object being visible and an object being audible?

There's not really a difference, conceptually. Your body element is considered by browsers to be something that needs to be visually rendered.

Your video element is still there and playing, except for the fact that it's not connected to anything that would cause the actual frames to render on your display.

In fact, if you create your video in JavaScript and never actually put it in the DOM tree somewhere under body, you could still "get" the visual data by, for example, plotting the video frame to a canvas in the visible DOM:

ctx.drawImage(videoElm, 0, 0);

In the case of an audio element, it won't render the controls, the seeking bar, etc. if you don't add it to the visible DOM.

However, in most cases, it'll still play to speakers (because the browsers connect the sound to the speakers regardless of the audio element being in the visible DOM on the display).

In many ways, having a <video> element that's not attached to your visible DOM is roughly equivalent to simply having it in your body with display: none; applied to it.



来源:https://stackoverflow.com/questions/26876256/why-are-videos-played-before-inserted-into-dom

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!