skip to Main Content

I am making a custom element that functions as a textarea with syntax highlighting and some other features. I’ve simplified it to a custom element with a div inside that gains height when clicking the page.

To make the custom element have a default height and be able to increase in height due to more content, I used min-height: 40vh. The problem is that height: 100% in the div inside the shadow dom doesn’t work. It doesn’t expand to 100% of the height of the shadow dom / the custom element’s inner height. However, the custom element does still grow to fit the div when needed.

Note that the custom element uses box-sizing: border-box and also has a border.

What I have tried:

I have tried all these on both Chrome and Firefox and none of them work as desired on either browser.

height: anything between 0 and 40vh on custom element

This works except the custom element doesn’t grow to fit the div when the div grows bigger than the custom element.

min-height: inherit on div

This works except it includes the border of the custom element parent, which makes the parent grow bigger than 40vh by default using box-sizing: border-box. I want the custom element to have min-height: 40vh with box-sizing: border-box.

Flexbox

Flexbox on the custom element doesn’t affect elements in the shadow root.

The code with the problem

Note that StackOverflow doesn’t give the snippet enough screen height to show the problem, and clicking "Expand snippet" and then running it has the same problem, so make sure to first start it and then click "Full page", and then clicking the page will make the div grow.

<!DOCTYPE html>
<html>
    <head>
        <style>
            * {
                box-sizing: border-box;
                margin: 0;
                padding: 0;
            }
            html, body {
                height: 100%;
            }
            my-element {
                border: 40px solid gray;
                min-height: 40vh;
            }
        </style>
    </head>
    <body>
        <my-element></my-element>

        <script>
            class MyElement extends HTMLElement {
                constructor() {
                    super();
        
                    const shadow = this.attachShadow({ mode: "open" });
                    const stylesheet = new CSSStyleSheet();
                    stylesheet.replaceSync(`
                        * {
                            margin: 0;
                            padding: 0;
                        }
                        :host {
                            display: block;
                        }
                        div {
                            background: green;
                            width: 100%;
                            height: 100%; /* not working */
                        }
                    `);
                    shadow.adoptedStyleSheets = [stylesheet];

                    const div = document.createElement("div");
                    div.innerText = "Hello";
                    shadow.appendChild(div);
                    document.body.onclick = () => {
                        div.innerText += "nHi";
                    };
                }
            }
            customElements.define("my-element", MyElement);
        </script>
    </body>
</html>

I could make a hacky solution using JS but I want to see if there is a way to do this using just CSS.

2

Answers


  1. Your div is occupying 100% of the height of its parent element, which is my-element. If you want it to occupy the entirety of the screen, then you will need to set its parent to have 100% height.

    <!DOCTYPE html>
    <html>
        <head>
            <style>
                * {
                    box-sizing: border-box;
                    margin: 0;
                    padding: 0;
                }
                html, body {
                    height: 100%;
                }
                my-element {
                    border: 40px solid gray;
                    min-height: 40vh;
                    height: 100%;
                }
            </style>
        </head>
        <body>
            <my-element></my-element>
    
            <script>
                class MyElement extends HTMLElement {
                    constructor() {
                        super();
            
                        const shadow = this.attachShadow({ mode: "open" });
                        const stylesheet = new CSSStyleSheet();
                        stylesheet.replaceSync(`
                            * {
                                margin: 0;
                                padding: 0;
                            }
                            :host {
                                display: block;
                            }
                            div {
                                background: green;
                                width: 100%;
                                height: 100%; /* not working */
                            }
                        `);
                        shadow.adoptedStyleSheets = [stylesheet];
    
                        const div = document.createElement("div");
                        div.innerText = "Hello";
                        shadow.appendChild(div);
                        document.body.onclick = () => {
                            div.innerText += "nHi";
                        };
                    }
                }
                customElements.define("my-element", MyElement);
            </script>
        </body>
    </html>
    Login or Signup to reply.
  2. If it can’t be done with pure CSS, and you do need JavaScript,
    then I would toggle a <STYLE> inside shadowDOM

    • If the content is less than the Component clientHeight enable that <STYLE>

    • When the content overflows the Web Component, disable the <STYLE>

    This way you can add more styling (border-color:blue in below example)
    And you are not sticking inline styles on an element.

    https://jsfiddle.net/WebComponents/w704hdvu/

    I refactored your code so it can run as inline SO snippet

    Initial state of the <STYLE> is disabled to show the RED background.
    After first click it shows correct state.

    <style>
      my-element {
        box-sizing: border-box; border: 10px solid gray;
        min-height: 120px; display: block;
      }
    </style>
    <my-element></my-element>
    <script>
      const element = (t,p={}) => Object.assign(document.createElement(t), p);
      customElements.define("my-element", class extends HTMLElement {
        constructor() {
          super()
            .attachShadow({ mode: "open" })
            .append( 
              element("STYLE", { innerHTML: `:host { background:red } #container { background:green }` }),
              this.heightStyle = element("STYLE",{
                initialHeight : this.clientHeight, // might as well store it here
                innerHTML : `:host { border-color:blue!important} 
                             #container{ height:${this.clientHeight}px }`,
                onload : () => this.heightStyle.disabled = true,// demo only, show red
              }),
              this.container = element("DIV", { id : "container", 
                                                onclick : () => this.addLine(),
                                                innerHTML: "Hello Web Component (click me)" }) );
          this.addLine();
        }
        addLine( newdiv = element("DIV",{ innerText:"FORCE LINE HEIGHT" }) , 
                 container=this.container, style=this.heightStyle ){
          style.disabled = true; // get correct #container height
          container.append( newdiv );
          newdiv.innerText = "clientHeight:" + this.clientHeight + "px";    
          style.disabled = container.clientHeight > style.initialHeight;
        }
      });
    </script>
    Click GREEN 
    Green DIV should "cover" the red background, but also overflow
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search