aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--js/src/tooltip.js3
-rw-r--r--js/src/util.js23
-rw-r--r--js/tests/unit/util.js38
-rw-r--r--js/tests/visual/tooltip.html22
-rw-r--r--site/docs/4.1/components/popovers.md1
-rw-r--r--site/docs/4.1/components/tooltips.md1
6 files changed, 86 insertions, 2 deletions
diff --git a/js/src/tooltip.js b/js/src/tooltip.js
index 1c40dfed3..9fa53a71b 100644
--- a/js/src/tooltip.js
+++ b/js/src/tooltip.js
@@ -244,8 +244,9 @@ class Tooltip {
if (this.isWithContent() && this._isEnabled) {
$(this.element).trigger(showEvent)
+ const shadowRoot = Util.findShadowRoot(this.element)
const isInTheDom = $.contains(
- this.element.ownerDocument.documentElement,
+ shadowRoot !== null ? shadowRoot : this.element.ownerDocument.documentElement,
this.element
)
diff --git a/js/src/util.js b/js/src/util.js
index e9665d24f..1d804cf9d 100644
--- a/js/src/util.js
+++ b/js/src/util.js
@@ -142,6 +142,29 @@ const Util = {
}
}
}
+ },
+
+ findShadowRoot(element) {
+ if (!document.documentElement.attachShadow) {
+ return null
+ }
+
+ // Can find the shadow root otherwise it'll return the document
+ if (typeof element.getRootNode === 'function') {
+ const root = element.getRootNode()
+ return root instanceof ShadowRoot ? root : null
+ }
+
+ if (element instanceof ShadowRoot) {
+ return element
+ }
+
+ // when we don't find a shadow root
+ if (!element.parentNode) {
+ return null
+ }
+
+ return Util.findShadowRoot(element.parentNode)
}
}
diff --git a/js/tests/unit/util.js b/js/tests/unit/util.js
index 61727304d..2fd6f6b7c 100644
--- a/js/tests/unit/util.js
+++ b/js/tests/unit/util.js
@@ -124,4 +124,42 @@ $(function () {
assert.expect(1)
assert.ok(Util.supportsTransitionEnd())
})
+
+ QUnit.test('Util.findShadowRoot should find the shadow DOM root', function (assert) {
+ // Only for newer browsers
+ if (!document.documentElement.attachShadow) {
+ assert.expect(0)
+ return
+ }
+
+ assert.expect(2)
+ var $div = $('<div id="test"></div>').appendTo($('#qunit-fixture'))
+ var shadowRoot = $div[0].attachShadow({
+ mode: 'open'
+ })
+ console.warn($div[0].attachShadow, shadowRoot)
+
+ assert.equal(shadowRoot, Util.findShadowRoot(shadowRoot))
+ shadowRoot.innerHTML = '<button>Shadow Button</button>'
+ assert.equal(shadowRoot, Util.findShadowRoot(shadowRoot.firstChild))
+ })
+
+ QUnit.test('Util.findShadowRoot should return null when attachShadow is not available', function (assert) {
+ assert.expect(1)
+
+ var $div = $('<div id="test"></div>').appendTo($('#qunit-fixture'))
+ if (!document.documentElement.attachShadow) {
+ assert.equal(null, Util.findShadowRoot($div[0]))
+ } else {
+ var sandbox = sinon.createSandbox()
+
+ sandbox.replace(document.documentElement, 'attachShadow', function () {
+ // to avoid empty function
+ return $div
+ })
+
+ assert.equal(null, Util.findShadowRoot($div[0]))
+ sandbox.restore()
+ }
+ })
})
diff --git a/js/tests/visual/tooltip.html b/js/tests/visual/tooltip.html
index 436f5c1cd..831f26d0a 100644
--- a/js/tests/visual/tooltip.html
+++ b/js/tests/visual/tooltip.html
@@ -62,7 +62,12 @@
</button>
</p>
</div>
- <div id="target" title="Test tooltip on transformed element"></div>
+ <div class="row">
+ <div class="col-sm-3">
+ <div id="target" title="Test tooltip on transformed element"></div>
+ </div>
+ <div id="shadow" class="pt-5"></div>
+ </div>
<div id="customContainer"></div>
</div>
@@ -72,6 +77,21 @@
<script src="../../dist/tooltip.js"></script>
<script>
$(function () {
+ if (typeof document.body.attachShadow === 'function') {
+ var shadowRoot = $('#shadow')[0].attachShadow({ mode: 'open' })
+ shadowRoot.innerHTML =
+ '<button type="button" class="btn btn-secondary" data-toggle="tooltip" data-placement="top" title="Tooltip on top in a shadow dom">' +
+ ' Tooltip on top in a shadow dom' +
+ '</button>' +
+ '<button id="secondTooltip" type="button" class="btn btn-secondary" data-toggle="tooltip" data-placement="top" title="Tooltip on top in a shadow dom with container option">' +
+ ' Tooltip on top in a shadow dom' +
+ '</button>'
+
+ $(shadowRoot.firstChild).tooltip()
+ $(shadowRoot.getElementById('secondTooltip')).tooltip({
+ container: shadowRoot
+ })
+ }
$('[data-toggle="tooltip"]').tooltip()
$('#tooltipElement').tooltip({
container: $('#customContainer')[0]
diff --git a/site/docs/4.1/components/popovers.md b/site/docs/4.1/components/popovers.md
index f55826d7c..1d407a901 100644
--- a/site/docs/4.1/components/popovers.md
+++ b/site/docs/4.1/components/popovers.md
@@ -20,6 +20,7 @@ Things to know when using the popover plugin:
- Popovers for `.disabled` or `disabled` elements must be triggered on a wrapper element.
- When triggered from anchors that wrap across multiple lines, popovers will be centered between the anchors' overall width. Use `.text-nowrap` on your `<a>`s to avoid this behavior.
- Popovers must be hidden before their corresponding elements have been removed from the DOM.
+- Popovers can be triggered thanks to an element inside a shadow DOM.
{% include callout-info-prefersreducedmotion.md %}
diff --git a/site/docs/4.1/components/tooltips.md b/site/docs/4.1/components/tooltips.md
index aa3ebe077..e49db5634 100644
--- a/site/docs/4.1/components/tooltips.md
+++ b/site/docs/4.1/components/tooltips.md
@@ -19,6 +19,7 @@ Things to know when using the tooltip plugin:
- Tooltips for `.disabled` or `disabled` elements must be triggered on a wrapper element.
- When triggered from hyperlinks that span multiple lines, tooltips will be centered. Use `white-space: nowrap;` on your `<a>`s to avoid this behavior.
- Tooltips must be hidden before their corresponding elements have been removed from the DOM.
+- Tooltips can be triggered thanks to an element inside a shadow DOM.
{% include callout-info-prefersreducedmotion.md %}