Search code examples
javascripthtmlweb-componentnative-web-component

How Web component set values to it's child?


I was trying to create a web component. The component just had a progress. the following is my code.

class MyElement extends HTMLElement {
        static get observedAttributes() {
            return ['value'];
        }

        get value() {
            return this.hasAttribute('value');
        }

        set value(val) {
            if(val) {
                this.setAttribute('value', val);
            }
            else {
                this.removeAttribute('value')
            }

            setValue(val);
        }

        constructor() {
            super();
            let shadowRoot = this.attachShadow({mode: 'open'});
            const t = document.querySelector('#my-element');
            const instance = t.content.cloneNode(true);
            shadowRoot.appendChild(instance);
            this.shadowDOM = shadowRoot;
        }

        attributeChangedCallback(name, oldValue, newValue) {
            if (this.value) {
                this.setValue(newValue);
            }
        }

        // Set a value to progress
        setValue(val) {
            // How to do?
        }
}

customElements.define('my-element', MyElement);
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>index</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>

    <div>
        <my-element value=50></my-element>
    </div>

    <button onclick="test()" style="bottom: 10px; right: 10px; position: absolute;">test</button>

    <script>
        function test() {
            // How to set the value to my-element?
        }
    </script>


    <!--My Element-->
    <template id="my-element">
        <style>
            :host {
                position: relative;
                display: block;
                width: 600px;
            }
            progress {
                -webkit-appearance: none;
                -moz-appearance: none;
            }
                
            progress::-moz-progress-bar {
                background: gainsboro;
                width: 300px;
                height: 500px;
            }
                
            progress::-moz-progress-value {
                background: green;
            }
                    
            progress::-webkit-progress-bar {
                background: gainsboro;
                width: 300px;
                height: 500px;
            }
                
            progress::-webkit-progress-value {
                background: green;
            }
            
        </style>
            
        <progress value=20 max=100>
        </progress>
    </template>
</body>
</html>

I wanted to control the progress value by the value of my-element.

I set a value in my-element(<my-element value=50></my-element>) but it's not work, and I didn't know how to set value by js.

I guessed the problem was the setValue(val) in my MyElement class, but I didn't know how to implement it.

Someone can help me? Thanks!


Solution

  • I would say you just need to use the DOM and retrieve your progress element.

    class MyElement extends HTMLElement {
            static get observedAttributes() {
                return ['value'];
            }
    
            get value() {
                return this.hasAttribute('value');
            }
    
            set value(val) {
                if(val) {
                    this.setAttribute('value', val);
                }
                else {
                    this.removeAttribute('value')
                }
            }
    
            constructor() {
                super();
                let shadowRoot = this.attachShadow({mode: 'open'});
                const t = document.querySelector('#my-element');
                const instance = t.content.cloneNode(true);
                shadowRoot.appendChild(instance);
                this.shadowDOM = shadowRoot;
            }
    
            attributeChangedCallback(name, oldValue, newValue) {
                if (this.value) {
                    this.setValue(newValue);
                }
            }
    
            // Set a value to progress
            setValue(val) {
                const progress = this.shadowDOM.lastElementChild;
                progress.value = val;
            }
    }
    
    customElements.define('my-element', MyElement);
    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="utf-8" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <title>index</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
    </head>
    <body>
    
        <div>
            <my-element id="myElement" value=50></my-element>
        </div>
    
        <button onclick="test()" style="bottom: 10px; right: 10px; position: absolute;">test</button>
    
        <script>
            function test() {
                document.getElementById("myElement").value = 5;
            }
        </script>
    
    
        <!--My Element-->
        <template id="my-element">
            <style>
                :host {
                    position: relative;
                    display: block;
                    width: 600px;
                }
                progress {
                    -webkit-appearance: none;
                    -moz-appearance: none;
                }
                    
                progress::-moz-progress-bar {
                    background: gainsboro;
                    width: 300px;
                    height: 500px;
                }
                    
                progress::-moz-progress-value {
                    background: green;
                }
                        
                progress::-webkit-progress-bar {
                    background: gainsboro;
                    width: 300px;
                    height: 500px;
                }
                    
                progress::-webkit-progress-value {
                    background: green;
                }
                
            </style>
                
            <progress value=20 max=100>
            </progress>
        </template>
    </body>
    </html>