问题
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 5089jQuery.parseHTML @ jquery-2.1.1.js
Line 8810jQuery.fn.init @ jquery-2.1.1.js
Line 2735jQuery @ jquery-2.1.1.js
Line 76(anonymous function) @ test.html
Line 28jQuery.Callbacks.fire @ jquery-2.1.1.js
Line 3073jQuery.Callbacks.self.fireWith @ jquery-2.1.1.js
Line 3185jQuery.extend.ready @ jquery-2.1.1.js
Line 3391completed @ 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