Home > Software design >  Issues with style-loader lazyloading for multiple elements in shadow-dom
Issues with style-loader lazyloading for multiple elements in shadow-dom

Time:01-23

I'm currently working with webpack and style-loader to inject my styles into components that use shadow-dom. However, the issue happens when I try to use multiple instances of the element (the styles stop injecting). I was able to properly solve this issue with another component by adding the unuse function to my disconnectCallback for that component. Unfortunately, for this component below, I expect it to appear multiple times at once on a page. Am I doing something wrong?

Component.tsx:

import React from 'react';
import { createRoot } from 'react-dom/client';

// Styles
import styles from '../styles/contentScript.css';

// Images
const icon = chrome.runtime.getURL('assets/icon.png');

export default class CustomButton extends HTMLElement {
    constructor() {
        super();
        this.attachShadow({ mode: 'open' });

        // Functions
        this.handleShow = this.handleShow.bind(this);
    }

    handleShow = (e: React.MouseEvent<HTMLButtonElement, MouseEvent>) => {
        ...
    };

    connectedCallback() {
        // Inject Styles
        styles.use({ target: this.shadowRoot });

        // Inject Component into ShadowRoot
        createRoot(this.shadowRoot).render(this.render());
    }

    render() {
        return (
            <div className='absolute inset-0'>
                <button className='main-button group' onClick={this.handleShow}>
                    <img src={icon} className='w-7' />
                    <span className='main-button-tooltip group-hover:scale-100'>
                        Open Popup
                    </span>
                </button>
            </div>
        );
    }

    disconnectedCallback() {
        styles.unuse();
    }
}

customElements.define('custom-button', CustomButton);

webpack.config.js

// Imports...
module.exports = {
    entry: {
        ...
    },
    module: {
        rules: [
            {
                use: 'ts-loader',
                test: /\.tsx?$/,
                exclude: /node_modules/,
            },
            {
                use: [
                    {
                        loader: 'style-loader',
                        options: {
                            injectType: 'lazyStyleTag',
                            insert: function insertIntoTarget(element, options) {
                                var parent = options.target || document.head;
                                parent.appendChild(element);
                            },
                        },
                    },
                    'css-loader',
                    {
                        loader: 'postcss-loader',
                        options: {
                            postcssOptions: {
                                indent: 'postcss',
                                plugins: [tailwindcss, autoprefixer],
                            },
                        },
                    },
                ],
                test: /\.css$/i,
            },
            {
                type: 'asset/resource',
                test: /\.(png|jpg|jpeg|gif)$/i,
            },
        ]
    },
    resolve: {
        ...
    },
    plugins: [
        ...
    ],
    output: {
        filename: '[name].js',
        clean: true,
    },
    optimization: {
        ...
    },
}

I should also note (in case it's important) I am using tailwind for styling so I've included postcss and autoprefixer. This is also for a chrome extension so I'm creating this component in my contentScript. I have also tried it without the unuse call in my disconnectCallback and faced the same issue.

CodePudding user response:

to follow the discussion on github, if you're only browser target is Chrome, then I really suggest you to use the CSSStyleSheet class.

In that case, you should drop from your webpack configuration the style-loader, as it's not needed anymore:

                    {
                        loader: 'style-loader',
                        options: {
                            injectType: 'lazyStyleTag',
                            insert: function insertIntoTarget(element, options) {
                                var parent = options.target || document.head;
                                parent.appendChild(element);
                            },
                        },
                    },

Then modify the configuration of the css-loader to have the option exportType = "css-style-sheet" (https://webpack.js.org/loaders/css-loader/#exporttype).

In this way, the exported element is already an object of type CSSStyleSheet, and you can use it directly on your web component:

import sheet from "./styles.css" assert { type: "css" };

document.adoptedStyleSheets = [sheet];
shadowRoot.adoptedStyleSheets = [sheet];

Normally, the 'postcss' step should not pose problems.

No need anymore to use "use / unuse" then (because that's an API of style-loder that you should remove with this solution)

  • Related