Skip to content

Commit

Permalink
fix(shadow-dom): prevent warning 'element supplied is not included' (#…
Browse files Browse the repository at this point in the history
…8192)

Co-authored-by: Giuseppe Piscopo <[email protected]>
  • Loading branch information
BrainCrumbz and GiuseppePiscopo authored Jun 6, 2023
1 parent 3dece50 commit dc1e2bb
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 2 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
"minify:css:cdn": "cleancss dist/alt/video-js-cdn.css -o dist/alt/video-js-cdn.min.css",
"minify:css:default": "cleancss dist/video-js.css -o dist/video-js.min.css",
"watch": "npm-run-all -p watch:*",
"watch:lang": "chokidar --initial 'lang/**/!(zh-Hans|zh-Hant)*.json' -c 'npm run build:lang'",
"watch:lang": "chokidar --initial \"lang/**/!(zh-Hans|zh-Hant)*.json\" -c \"npm run build:lang\"",
"watch:rollup": "rollup -c -w --no-progress",
"watch:types": "tsc -w",
"watch:css": "npm-run-all -p build:css:default build:css:cdn watch:css:*",
Expand Down
79 changes: 79 additions & 0 deletions sandbox/shadow-dom.html.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Video.js Sandbox</title>
<link href="../dist/video-js.css" rel="stylesheet" type="text/css">
<script src="../dist/video.js"></script>
<link rel="icon" href="data:;base64,=">
</head>
<body>
<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;">
<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>
<pre>npm start</pre>
<pre>open http://localhost:9999/sandbox/index.html</pre>
</div>

<p>Tap on video to play/pause</p>

<test-custom-element id="customElement1"></test-custom-element>

<script>
// Custom Element definition
class TestCustomElement extends HTMLElement {

constructor() {
super();

const shadowRoot = this.attachShadow({ mode: 'closed' });

const styleLinkElem = document.createElement('link');

styleLinkElem.setAttribute('rel', 'stylesheet');
styleLinkElem.setAttribute('href', '../dist/video-js.css')
shadowRoot.append(styleLinkElem);

const containerElem = document.createElement('div');

containerElem.setAttribute('data-vjs-player', '');
shadowRoot.appendChild(containerElem);

const videoElem = document.createElement('video');

videoElem.setAttribute('preload', 'auto');
videoElem.setAttribute('width', 640);
videoElem.setAttribute('height', 260);
containerElem.appendChild(videoElem);

const sourceElem = document.createElement('source');

sourceElem.setAttribute('src', 'https://vjs.zencdn.net/v/oceans.mp4');
sourceElem.setAttribute('type', 'video/mp4');
videoElem.appendChild(sourceElem);

this.innerPlayer = videojs(videoElem);

containerElem.addEventListener('click', () => {
if (this.innerPlayer.paused()) {
this.innerPlayer.play();
}
else {
this.innerPlayer.pause();
}
});
}
}

window.customElements.define('test-custom-element', TestCustomElement);

// Main entry point

document.addEventListener('DOMContentLoaded', () => {
var customElem = document.getElementById('customElement1');
var innerPlayer = customElem.innerPlayer;
innerPlayer.log('Shadow DOM inner player created', innerPlayer);
});
</script>

</body>
</html>
7 changes: 6 additions & 1 deletion src/js/video.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,12 @@ function videojs(id, options, ready) {
// This will make sure that the element is indeed in the dom of that document.
// Additionally, check that the document in question has a default view.
// If the document is no longer attached to the dom, the defaultView of the document will be null.
if (!el.ownerDocument.defaultView || !el.ownerDocument.body.contains(el)) {
// If element is inside Shadow DOM (e.g. is part of a Custom element), ownerDocument.body
// always returns false. Instead, use the Shadow DOM root.
const inShadowDom = el.getRootNode() instanceof window.ShadowRoot;
const rootNode = inShadowDom ? el.getRootNode() : el.ownerDocument.body;

if (!el.ownerDocument.defaultView || !rootNode.contains(el)) {
log.warn('The element supplied is not included in the DOM');
}

Expand Down
26 changes: 26 additions & 0 deletions test/unit/utils/custom-element.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/* eslint-env browser */
import videojs from '../../../src/js/video.js';

export class TestCustomElement extends HTMLElement {

constructor() {
super();

const shadowRoot = this.attachShadow({ mode: 'closed' });

const containerElem = document.createElement('div');

containerElem.setAttribute('data-vjs-player', '');
shadowRoot.appendChild(containerElem);

const videoElem = document.createElement('video');

videoElem.setAttribute('width', 640);
videoElem.setAttribute('height', 260);
containerElem.appendChild(videoElem);

this.innerPlayer = videojs(videoElem);
}
}

window.customElements.define('test-custom-element', TestCustomElement);
25 changes: 25 additions & 0 deletions test/unit/video.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import * as Dom from '../../src/js/utils/dom.js';
import log from '../../src/js/utils/log.js';
import document from 'global/document';
import sinon from 'sinon';
// import custom element for Shadow DOM test
import './utils/custom-element.test';

QUnit.module('video.js', {
beforeEach() {
Expand Down Expand Up @@ -84,6 +86,29 @@ QUnit.test(
}
);

QUnit.test(
'should not log if the supplied element is included in the Shadow DOM',
function(assert) {
const origWarnLog = log.warn;
const fixture = document.getElementById('qunit-fixture');
const warnLogs = [];

log.warn = (args) => {
warnLogs.push(args);
};

const customElem = document.createElement('test-custom-element');

fixture.appendChild(customElem);
const innerPlayer = customElem.innerPlayer;

assert.ok(innerPlayer, 'created player within Shadow DOM');
assert.equal(warnLogs.length, 0, 'no warn logs');

log.warn = origWarnLog;
}
);

QUnit.test(
'should log about already initialized players if options already passed',
function(assert) {
Expand Down

0 comments on commit dc1e2bb

Please sign in to comment.