⏹工具类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" />
<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>日历demotitle>
<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">Englishion-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-buttons slot="buttons">
<ion-button id="confirmBtn">Good to go!ion-button>
<ion-button id="cancelBtn">Resetion-button>
ion-buttons>
ion-datetime>
<ion-datetime presentation="time">ion-datetime>
<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 = `
自定义
`;
$(firstGrid).append(customEle);
// 7.⏹⏹⏹参考官方文档,为添加的额外按钮添加新的事件
const confirmBtn = document.querySelector('#confirmBtn');
$(confirmBtn).click(function() {
datetimeComponent.confirm();
})
$(cancelBtn).click(function() {
datetimeComponent.reset();
})
}
script>
html>
参考资料:
[译] 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