Skip to content

Commit dc1e2bb

Browse files
fix(shadow-dom): prevent warning 'element supplied is not included' (#8192)
Co-authored-by: Giuseppe Piscopo <[email protected]>
1 parent 3dece50 commit dc1e2bb

File tree

5 files changed

+137
-2
lines changed

5 files changed

+137
-2
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@
5252
"minify:css:cdn": "cleancss dist/alt/video-js-cdn.css -o dist/alt/video-js-cdn.min.css",
5353
"minify:css:default": "cleancss dist/video-js.css -o dist/video-js.min.css",
5454
"watch": "npm-run-all -p watch:*",
55-
"watch:lang": "chokidar --initial 'lang/**/!(zh-Hans|zh-Hant)*.json' -c 'npm run build:lang'",
55+
"watch:lang": "chokidar --initial \"lang/**/!(zh-Hans|zh-Hant)*.json\" -c \"npm run build:lang\"",
5656
"watch:rollup": "rollup -c -w --no-progress",
5757
"watch:types": "tsc -w",
5858
"watch:css": "npm-run-all -p build:css:default build:css:cdn watch:css:*",

sandbox/shadow-dom.html.example

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
<title>Video.js Sandbox</title>
6+
<link href="../dist/video-js.css" rel="stylesheet" type="text/css">
7+
<script src="../dist/video.js"></script>
8+
<link rel="icon" href="data:;base64,=">
9+
</head>
10+
<body>
11+
<div style="background-color:#eee; border: 1px solid #777; padding: 10px; margin-bottom: 20px; font-size: .8em; line-height: 1.5em; font-family: Verdana, sans-serif;">
12+
<p>You can use /sandbox/ for writing and testing your own code. Nothing in /sandbox/ will get checked into the repo, except files that end in .example (so don't edit or add those files). To get started run `npm start` and open the index.html</p>
13+
<pre>npm start</pre>
14+
<pre>open http://localhost:9999/sandbox/index.html</pre>
15+
</div>
16+
17+
<p>Tap on video to play/pause</p>
18+
19+
<test-custom-element id="customElement1"></test-custom-element>
20+
21+
<script>
22+
// Custom Element definition
23+
class TestCustomElement extends HTMLElement {
24+
25+
constructor() {
26+
super();
27+
28+
const shadowRoot = this.attachShadow({ mode: 'closed' });
29+
30+
const styleLinkElem = document.createElement('link');
31+
32+
styleLinkElem.setAttribute('rel', 'stylesheet');
33+
styleLinkElem.setAttribute('href', '../dist/video-js.css')
34+
shadowRoot.append(styleLinkElem);
35+
36+
const containerElem = document.createElement('div');
37+
38+
containerElem.setAttribute('data-vjs-player', '');
39+
shadowRoot.appendChild(containerElem);
40+
41+
const videoElem = document.createElement('video');
42+
43+
videoElem.setAttribute('preload', 'auto');
44+
videoElem.setAttribute('width', 640);
45+
videoElem.setAttribute('height', 260);
46+
containerElem.appendChild(videoElem);
47+
48+
const sourceElem = document.createElement('source');
49+
50+
sourceElem.setAttribute('src', 'https://vjs.zencdn.net/v/oceans.mp4');
51+
sourceElem.setAttribute('type', 'video/mp4');
52+
videoElem.appendChild(sourceElem);
53+
54+
this.innerPlayer = videojs(videoElem);
55+
56+
containerElem.addEventListener('click', () => {
57+
if (this.innerPlayer.paused()) {
58+
this.innerPlayer.play();
59+
}
60+
else {
61+
this.innerPlayer.pause();
62+
}
63+
});
64+
}
65+
}
66+
67+
window.customElements.define('test-custom-element', TestCustomElement);
68+
69+
// Main entry point
70+
71+
document.addEventListener('DOMContentLoaded', () => {
72+
var customElem = document.getElementById('customElement1');
73+
var innerPlayer = customElem.innerPlayer;
74+
innerPlayer.log('Shadow DOM inner player created', innerPlayer);
75+
});
76+
</script>
77+
78+
</body>
79+
</html>

src/js/video.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,12 @@ function videojs(id, options, ready) {
149149
// This will make sure that the element is indeed in the dom of that document.
150150
// Additionally, check that the document in question has a default view.
151151
// If the document is no longer attached to the dom, the defaultView of the document will be null.
152-
if (!el.ownerDocument.defaultView || !el.ownerDocument.body.contains(el)) {
152+
// If element is inside Shadow DOM (e.g. is part of a Custom element), ownerDocument.body
153+
// always returns false. Instead, use the Shadow DOM root.
154+
const inShadowDom = el.getRootNode() instanceof window.ShadowRoot;
155+
const rootNode = inShadowDom ? el.getRootNode() : el.ownerDocument.body;
156+
157+
if (!el.ownerDocument.defaultView || !rootNode.contains(el)) {
153158
log.warn('The element supplied is not included in the DOM');
154159
}
155160

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/* eslint-env browser */
2+
import videojs from '../../../src/js/video.js';
3+
4+
export class TestCustomElement extends HTMLElement {
5+
6+
constructor() {
7+
super();
8+
9+
const shadowRoot = this.attachShadow({ mode: 'closed' });
10+
11+
const containerElem = document.createElement('div');
12+
13+
containerElem.setAttribute('data-vjs-player', '');
14+
shadowRoot.appendChild(containerElem);
15+
16+
const videoElem = document.createElement('video');
17+
18+
videoElem.setAttribute('width', 640);
19+
videoElem.setAttribute('height', 260);
20+
containerElem.appendChild(videoElem);
21+
22+
this.innerPlayer = videojs(videoElem);
23+
}
24+
}
25+
26+
window.customElements.define('test-custom-element', TestCustomElement);

test/unit/video.test.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import * as Dom from '../../src/js/utils/dom.js';
44
import log from '../../src/js/utils/log.js';
55
import document from 'global/document';
66
import sinon from 'sinon';
7+
// import custom element for Shadow DOM test
8+
import './utils/custom-element.test';
79

810
QUnit.module('video.js', {
911
beforeEach() {
@@ -84,6 +86,29 @@ QUnit.test(
8486
}
8587
);
8688

89+
QUnit.test(
90+
'should not log if the supplied element is included in the Shadow DOM',
91+
function(assert) {
92+
const origWarnLog = log.warn;
93+
const fixture = document.getElementById('qunit-fixture');
94+
const warnLogs = [];
95+
96+
log.warn = (args) => {
97+
warnLogs.push(args);
98+
};
99+
100+
const customElem = document.createElement('test-custom-element');
101+
102+
fixture.appendChild(customElem);
103+
const innerPlayer = customElem.innerPlayer;
104+
105+
assert.ok(innerPlayer, 'created player within Shadow DOM');
106+
assert.equal(warnLogs.length, 0, 'no warn logs');
107+
108+
log.warn = origWarnLog;
109+
}
110+
);
111+
87112
QUnit.test(
88113
'should log about already initialized players if options already passed',
89114
function(assert) {

0 commit comments

Comments
 (0)