JS ShadowDOM组件修改样式,添加事件

⏹工具类utils.js

// 修改ShadowDOM样式
function changeShadowDOMStyles(shadowRootElement, styles, insertBeforeSelector) {

    const root = shadowRootElement?.shadowRoot;
    if (!root) {
        return;
    }

    // 如果组件中既存的样式和从外部传入的样式一致的话,就不修改组件样式
    const styleElements = root.querySelectorAll('style');
    const result = Array.from(styleElements).some(el => el.innerHTML === styles);
    if(result) {
        return;
    }
  
    // 创建style标签,并将样式标签添加到shadowDOM中
    const newStyleTag = document.createElement('style');
    newStyleTag.innerHTML = styles;

    /*
        如果insertBeforeSelector为空的话,
        默认将创建的style标签插入shadow-root的最底部
        如果指定了insertBeforeSelector的话,将style标签插入到指定的DOM元素之前
    */
    root.insertBefore(newStyleTag, root.querySelector(insertBeforeSelector));
}

// 休眠函数
const waitTime = async (milliseconds) => await new Promise((resolve) => setTimeout(resolve, milliseconds));

export {
    changeShadowDOMStyles,
    waitTime
}

⏹demo

<!DOCTYPE html>
<html lang="en" md="">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="format-detection" content="telephone=no">
    <link rel="stylesheet" href="./css/ionic.bundle.css" />
    <link rel="stylesheet" href="./css/init.css" />
    <link rel="stylesheet" href="./css/common.css" />
    <!-- 通过模块的方式加载ionic的js -->
    <script type="module" src="https://cdn.jsdelivr.net/npm/@ionic/core/dist/ionic/ionic.esm.js"></script>
    <script src="./js/jquery-3.6.0.js"></script>
    <title>日历demo</title>
    <style>
        
    </style>
</head>
<body>
    <ion-app>
        <ion-fab id="btn-fab" vertical="bottom" horizontal="end"  slot="fixed" 
                style="display: none;position:fixed; bottom: 40px;">
            <ion-fab-button id="btnBack2Top">
                <ion-icon name="arrow-up-circle-outline"></ion-icon>
            </ion-fab-button>
        </ion-fab>

        <ion-page class="ion-page" id="main-content"> 
            <ion-header mode="md">
                <ion-toolbar color="primary">
                    <ion-title>
                        demo系统
                        <div class="language">
                            <ion-icon name="globe" size="large"></ion-icon>
                            <ion-select id="selLanguage" value="jp" slot="end" interface="popover">
                                <ion-select-option value="jp" selected>日本語</ion-select-option>
                                <ion-select-option value="eng">English</ion-select-option>
                                <ion-select-option value="zn">中文</ion-select-option>
                            </ion-select>
                        </div>
                    </ion-title>
                </ion-toolbar>
            </ion-header>

            <!-- 各画面的处理部分 -->
            <ion-datetime id="custom-datetime" presentation="date" locale="en-GB">
                <!-- 
                    参考官方文档,为ion-datetime添加额外的按钮,其中slot指定插槽的名称
                 -->
                <ion-buttons slot="buttons">
                    <ion-button id="confirmBtn">Good to go!</ion-button>
                    <ion-button id="cancelBtn">Reset</ion-button>
                </ion-buttons>
            </ion-datetime>

            <ion-datetime presentation="time"></ion-datetime>

            <!-- Fade Footer -->
            <ion-footer>
                <ion-toolbar class="copyright" color="primary">
                    Copyright © Matsudo City, All rights reserved.
                </ion-toolbar>
            </ion-footer>   
        </ion-page>
    </ion-app>
</body>
<script type="module">
    /*
    _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
        1. 本demo使用了ionic的组件,组件均使用了 shadow-root 技术来封装组件
        2. script标签使用module之后,才能使用import导入模块
        3. 将修改样式的方法抽取为共通,导入
        4. ES6的模块,无法在静态html中使用,必须发布之后才能使用,否则会报错
        5. 可以使用 Live Server 来启动发布html
    _/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
    */
    import { changeShadowDOMStyles, waitTime } from './js/utils.js'

    window.onload = async function() {

        /*
            各组件的HTML都是动态加载到页面上
            让程序休眠50毫秒,否则无法获取到页面的DOM
        */ 
        await waitTime(50);

        // 获取ion-datetime组件的DOM对象
        const datetimeComponent = document.querySelector("ion-datetime");
        // 1.⏹⏹⏹从datetime组件中获取出shadowRoot
        const shadowRoot = datetimeComponent.shadowRoot;
        // 可以看到shadowRoot对象中的各个属性
        console.dir(shadowRoot);    

        /*
            shadowRoot相当于主DOM中的子DOM
            获取子DOM中的元素,需要再次使用选择器
        */ 
        const nodeList = shadowRoot.querySelectorAll(".calendar-month-grid");
        console.log(nodeList);

        // 从shadowDOM中获取出今天的日期对象
        const activeDay = shadowRoot.querySelector(".calendar-month-grid > .calendar-day-active");
        console.log(activeDay);

        // 2.⏹⏹⏹为今天的日期绑定单击事件
        $(activeDay).click(function(event) {

            console.dir(event);
            const {
                target: {
                    innerText
                }
            } =  event;
            alert(`今天的日期是${innerText}`);
        })

        // 3.⏹⏹⏹修改组件中的样式🥰🥰🥰
        // 准备好要覆盖的样式,必须要使用 !important 来覆盖
        // .calendar-month-grid 是原生组件中的class
        const styles1 = `
            .calendar-month-grid {
                background: #ffc409 !important;
            }
        `;
        /*
            参数1: shadowRoot对象
            参数2: 覆盖的样式
            参数3: 选择器,指定生成的Style标签在shadow-root的那个元素之前生成
        */
        changeShadowDOMStyles(datetimeComponent, styles1);
        
        // 4.⏹⏹⏹修改样式
        const styles2 = `
            .calendar-day-active {
                color: blue !important;
            }
            .calendar-month-grid button {
                border: 1px solid blue !important;
                border-radius: 16px;
            }
        `
        // 指定styles2这些样式插入在.datetime-calendar类所在的DOM之前
        changeShadowDOMStyles(datetimeComponent, styles2, ".datetime-calendar");

        // 5.⏹⏹⏹结合JQuery来修改shadowDOM元素中的属性,将其设置为不可用
        // $(shadowRoot).find(".calendar-month-year > ion-item").attr("disabled", "disabled");

        // 6.⏹⏹⏹将自定义DOM元素添加到组件中
        const calendarMonth = $(shadowRoot).find('.calendar-month-grid')[1];
        const firstGrid = $(calendarMonth).find('button')[0];
        const customEle = `
            <span style="color:red;">自定义<span>
        `;
        $(firstGrid).append(customEle);

        // 7.⏹⏹⏹参考官方文档,为添加的额外按钮添加新的事件
        const confirmBtn = document.querySelector('#confirmBtn');
        $(confirmBtn).click(function() {
            datetimeComponent.confirm();
        })

        $(cancelBtn).click(function() {
            datetimeComponent.reset();
        })
    }
</script>
</html>
  • 由于ShadowDOM和LightDOM做了隔离,因此组件的样式无法直接修改
  • 可以将css样式通过style标签的形式插入到ShadowDOM中,
    从而修改组件中的样式,插入之后的效果如下图
    在这里插入图片描述

😑修改前
在这里插入图片描述
🤨改修后
在这里插入图片描述


参考资料:
[译] Web Components简介
https://juejin.cn/post/6844903807734775816
[译] 编写可以复用的 HTML 模板
https://juejin.cn/post/6844903813116067853
[译] 从 0 创建自定义元素
https://github.com/xitu/gold-miner/blob/master/TODO1/creating-a-custom-element-from-scratch.md
[译] 使用 Shadow DOM 封装样式和结构
https://juejin.cn/post/6844903821550829581
[译] Web Components 的高级工具
https://juejin.cn/post/6844903816865775630
日本人的demo
https://qiita.com/alangdm/items/cec32f21151a9da3c3f2
https://github.com/alangdm/shadow-dom-quiz
https://alangdm.github.io/shadow-dom-quiz/
神秘的 shadow-dom 浅析
https://www.cnblogs.com/Red-ButterFly/p/7661996.html
【shadow dom入UI】web components思想如何应用于实际项目
https://www.cnblogs.com/yexiaochai/p/4167554.html
Angular之 ViewEncapsulation 与 Shadow DOM
https://www.cnblogs.com/xianrongbin/p/10931064.html
博客园 shadow DOM
https://recomm.cnblogs.com/blogpost/5040504
shadow-dom-inject-styles 样式修改插件
https://github.com/adamlacombe/Shadow-DOM-inject-styles
https://www.npmjs.com/package/shadow-dom-inject-styles
官方shadow_DOM案例
https://developer.mozilla.org/zh-CN/docs/Web/Web_Components/Using_shadow_DOM
https://developer.mozilla.org/zh-CN/docs/Web/API/ShadowRoot
ionic--修改默认组件样式
https://blog.csdn.net/weixin_43964866/article/details/108534242
ionic4关于CSS Custom Properties修改组件样式
https://www.jianshu.com/p/3a9423a2c94c
ShadowDOM css样式处理详解
https://cloud.tencent.com/developer/article/1965869

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值