diff --git a/package.json b/package.json index ecc8c2fea..cf5032399 100644 --- a/package.json +++ b/package.json @@ -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:*", diff --git a/sandbox/shadow-dom.html.example b/sandbox/shadow-dom.html.example new file mode 100644 index 000000000..90ea34897 --- /dev/null +++ b/sandbox/shadow-dom.html.example @@ -0,0 +1,79 @@ + + + + + Video.js Sandbox + + + + + +
+

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

+
npm start
+
open http://localhost:9999/sandbox/index.html
+
+ +

Tap on video to play/pause

+ + + + + + + diff --git a/src/js/video.js b/src/js/video.js index 2734cf08d..12a3d8235 100644 --- a/src/js/video.js +++ b/src/js/video.js @@ -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'); } diff --git a/test/unit/utils/custom-element.test.js b/test/unit/utils/custom-element.test.js new file mode 100644 index 000000000..a60f793da --- /dev/null +++ b/test/unit/utils/custom-element.test.js @@ -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); diff --git a/test/unit/video.test.js b/test/unit/video.test.js index 2b041b9ba..327437b0d 100644 --- a/test/unit/video.test.js +++ b/test/unit/video.test.js @@ -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() { @@ -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) {