user578895
user578895

Reputation:

Is it possible for CSS to escape the shadow dom (Polymer)?

I have a drop-down element in Polymer. Because of z-indexing issues, I need to pull the drop-down part out and make it a direct child of <body>. Unfortunately, I can't figure out how to do that and still be able to actually style the element. The only solution I can come up with is to make the drop-down portion its own polymer tag, but that seems really... backwards.

In short, I have an element:

<my-element>
    <template>
        <style>...</style>

        <input>
        <div id="dropdown">...</div>
    </template>
</my-element>

And then JS in the element that does document.body.appendChild( this.$.dropdown )

I'm trying to figure out how to stylize the drop-down once its been moved, but Polymer/Web Components' CSS isolation is preventing me from doing that.

Upvotes: 2

Views: 834

Answers (1)

user578895
user578895

Reputation:

[edit] Note that polymer can't properly handle on-* event handlers attached via the DOM with this method due to the fact that it can't walk the DOM tree back up to the polymer-element. So events need to be manually attached to anything using this.

I created a custom element that can be inserted into any element and it will pull out all rules that start with body and promote them to a new stylesheet inserted into head. There is a bit of work to be done, specifically around browsers that need to be polyfilled as the ::shadow selector won't work. But I'll get to that shortly.

<link rel="import" href="../polymer/polymer.html" />
<polymer-element name="promote-body-css">
    <script>
        'use strict';

        ~function(){
            var promoted = {};

            function promote( node ){
                var nodeName = node.nodeName;

                if( promoted[ nodeName ] ){ return; }

                var promoteRules = [];

                [].forEach.call( node.querySelectorAll( '::shadow style' ), function( stylesheet ){
                    [].forEach.call( stylesheet.sheet.cssRules, function( rule ){
                        if( rule.cssText.indexOf( 'body ' ) === 0 ){
                            promoteRules.push( rule.cssText );
                        }
                    } );
                } );

                if( promoteRules.length ){
                    var stylesheet = document.createElement( 'style' );
                    stylesheet.innerHTML = promoteRules.join( '' ) + '/* Inserted from polymer element ' + nodeName + ' */';

                    document.getElementsByTagName( 'head' )[0].appendChild( stylesheet );
                }
            }

            promoted[ nodeName ] = 1;

            Polymer( 'promote-body-css', {
                attached : function( parent ){
                    promote( ( parent = this.parentNode ).host || parent );
                }
            } );
        }();
    </script>
</polymer-element>

Upvotes: 1

Related Questions