From 9b77def560212d12fef2d0b9c12aa50727e3e5d7 Mon Sep 17 00:00:00 2001 From: Saptak Sengupta Date: Fri, 9 Nov 2018 16:45:31 +0530 Subject: [PATCH] Core: Recognize Shadow DOM in attachment checks Allow `isAttached` to check Shadow DOM for attachment. Fixes gh-3504 Closes gh-3996 Ref gh-3977 --- src/core/isAttached.js | 22 +++++++++ src/css/curCSS.js | 2 +- src/css/var/isHiddenWithinTree.js | 2 +- src/manipulation.js | 2 +- src/manipulation/buildFragment.js | 2 +- src/var/isAttached.js | 11 ----- test/unit/css.js | 80 +++++++++++++++++++++++++++++++ test/unit/effects.js | 32 +++++++++++++ 8 files changed, 138 insertions(+), 15 deletions(-) create mode 100644 src/core/isAttached.js delete mode 100644 src/var/isAttached.js diff --git a/src/core/isAttached.js b/src/core/isAttached.js new file mode 100644 index 000000000..efa2465a9 --- /dev/null +++ b/src/core/isAttached.js @@ -0,0 +1,22 @@ +define( [ + "../core", + "../var/documentElement", + "../selector" // jQuery.contains +], function( jQuery, documentElement ) { + "use strict"; + + var isAttached = function( elem ) { + return jQuery.contains( elem.ownerDocument, elem ); + }, + composed = { composed: true }; + + // Check attachment across shadow DOM boundaries when possible (gh-3504) + if ( documentElement.attachShadow ) { + isAttached = function( elem ) { + return jQuery.contains( elem.ownerDocument, elem ) || + elem.getRootNode( composed ) === elem.ownerDocument; + }; + } + + return isAttached; +} ); diff --git a/src/css/curCSS.js b/src/css/curCSS.js index 7fed20f17..98a594a77 100644 --- a/src/css/curCSS.js +++ b/src/css/curCSS.js @@ -1,6 +1,6 @@ define( [ "../core", - "../var/isAttached", + "../core/isAttached", "./var/rboxStyle", "./var/rnumnonpx", "./var/getStyles", diff --git a/src/css/var/isHiddenWithinTree.js b/src/css/var/isHiddenWithinTree.js index fd963a031..0ab610e29 100644 --- a/src/css/var/isHiddenWithinTree.js +++ b/src/css/var/isHiddenWithinTree.js @@ -1,6 +1,6 @@ define( [ "../../core", - "../../var/isAttached" + "../../core/isAttached" // css is assumed ], function( jQuery, isAttached ) { diff --git a/src/manipulation.js b/src/manipulation.js index 3c6d59262..a0e93f7ef 100644 --- a/src/manipulation.js +++ b/src/manipulation.js @@ -1,6 +1,6 @@ define( [ "./core", - "./var/isAttached", + "./core/isAttached", "./var/concat", "./var/isFunction", "./var/push", diff --git a/src/manipulation/buildFragment.js b/src/manipulation/buildFragment.js index 34bcc70c2..40c2ed1dc 100644 --- a/src/manipulation/buildFragment.js +++ b/src/manipulation/buildFragment.js @@ -1,7 +1,7 @@ define( [ "../core", "../core/toType", - "../var/isAttached", + "../core/isAttached", "./var/rtagName", "./var/rscriptType", "./wrapMap", diff --git a/src/var/isAttached.js b/src/var/isAttached.js deleted file mode 100644 index 1dd798268..000000000 --- a/src/var/isAttached.js +++ /dev/null @@ -1,11 +0,0 @@ -define( [ - "../core", - "../selector" // Get jQuery.contains -], function( jQuery ) { - "use strict"; - - return function isAttached( obj ) { - return jQuery.contains( obj.ownerDocument, obj ); - }; - -} ); diff --git a/test/unit/css.js b/test/unit/css.js index 7e08105d2..a197b6f04 100644 --- a/test/unit/css.js +++ b/test/unit/css.js @@ -641,6 +641,63 @@ QUnit.test( "show/hide detached nodes", function( assert ) { span.remove(); } ); +QUnit[ document.body.attachShadow ? "test" : "skip" ]( "show/hide shadow child nodes", function( assert ) { + assert.expect( 28 ); + jQuery( "
" ).appendTo( "#qunit-fixture" ); + var shadowHost = document.querySelector( "#shadowHost" ); + var shadowRoot = shadowHost.attachShadow( { mode: "open" } ); + shadowRoot.innerHTML = "" + + "" + + "" + + "" + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + " " + + "" + + ""; + + var test = { + "div": "block", + "p": "block", + "a": "inline", + "code": "inline", + "pre": "block", + "span": "inline", + "table": "table", + "thead": "table-header-group", + "tbody": "table-row-group", + "tr": "table-row", + "th": "table-cell", + "td": "table-cell", + "ul": "block", + "li": "list-item" + }; + + jQuery.each( test, function( selector, expected ) { + var shadowChild = shadowRoot.querySelector( "#shadow" + selector ); + var $shadowChild = jQuery( shadowChild ); + assert.strictEqual( $shadowChild.css( "display" ), "none", "is hidden" ); + $shadowChild.show(); + assert.strictEqual( $shadowChild.css( "display" ), expected, "Show using correct display type for " + selector ); + } ); +} ); + QUnit.test( "hide hidden elements (bug #7141)", function( assert ) { assert.expect( 3 ); @@ -966,6 +1023,29 @@ QUnit[ jQuery.find.compile && jQuery.fn.toggle ? "test" : "skip" ]( "detached to "cascade-hidden element in detached tree" ); } ); +QUnit[ jQuery.find.compile && jQuery.fn.toggle && document.body.attachShadow ? "test" : "skip" ]( "shadow toggle()", function( assert ) { + assert.expect( 4 ); + jQuery( "
" ).appendTo( "#qunit-fixture" ); + var shadowHost = document.querySelector( "#shadowHost" ); + var shadowRoot = shadowHost.attachShadow( { mode: "open" } ); + shadowRoot.innerHTML = "" + + "" + + "" + + "
"; + var shadowChild = shadowRoot.querySelector( "#shadowChild" ); + var shadowHiddenChild = shadowRoot.querySelector( "#shadowHiddenChild" ); + + var $shadowChild = jQuery( shadowChild ); + assert.strictEqual( $shadowChild.css( "display" ), "block", "is visible" ); + $shadowChild.toggle(); + assert.strictEqual( $shadowChild.css( "display" ), "none", "is hidden" ); + + $shadowChild = jQuery( shadowHiddenChild ); + assert.strictEqual( $shadowChild.css( "display" ), "none", "is hidden" ); + $shadowChild.toggle(); + assert.strictEqual( $shadowChild.css( "display" ), "block", "is visible" ); +} ); + QUnit.test( "jQuery.css(elem, 'height') doesn't clear radio buttons (bug #1095)", function( assert ) { assert.expect( 4 ); diff --git a/test/unit/effects.js b/test/unit/effects.js index e297273e4..9e8838132 100644 --- a/test/unit/effects.js +++ b/test/unit/effects.js @@ -220,6 +220,38 @@ supportjQuery.each( hideOptions, function( type, setup ) { assert.expectJqData( this, $span, "olddisplay" ); } ); + + QUnit[ document.body.attachShadow ? "test" : "skip" ]( + "Persist correct display value - " + type + " hidden, shadow child", function( assert ) { + assert.expect( 3 ); + + jQuery( "
" ).appendTo( "#qunit-fixture" ); + + var shadowHost = document.querySelector( "#shadowHost" ); + var shadowRoot = shadowHost.attachShadow( { mode: "open" } ); + shadowRoot.innerHTML = "" + + ""; + var shadowChild = shadowRoot.querySelector( "#shadowChild" ); + + var $shadowChild = jQuery( shadowChild ); + var displayNone = "none"; + var display = "inline"; + var clock = this.clock; + + $shadowChild.fadeIn( 100, function() { + assert.equal( $shadowChild.css( "display" ), display, "Expecting shadow display: " + display ); + $shadowChild.fadeOut( 100, function() { + assert.equal( $shadowChild.css( "display" ), displayNone, "Expecting shadow display: " + displayNone ); + $shadowChild.fadeIn( 100, function() { + assert.equal( $shadowChild.css( "display" ), display, "Expecting shadow display: " + display ); + } ); + } ); + } ); + + clock.tick( 300 ); + + assert.expectJqData( this, $shadowChild, "olddisplay" ); + } ); } ); QUnit.test( "animate(Hash, Object, Function)", function( assert ) { -- 2.39.5