steven026 发表于 2023-7-24 15:58:16

【油猴脚本开发指南】魔改sweetalert2支持shadowRoot

# 前言
阅读本文前需要有一定原生JS基础
## 建议阅读
### 基本原理

https://developer.mozilla.org/zh-CN/docs/Web/API/Web_components/Using_shadow_DOM

[油猴脚本引用库基本原理]
https://learn.scriptcat.org/docs/category/%E5%BC%95%E7%94%A8%E5%BA%93%E4%BD%BF%E7%94%A8/

### 基本用法
[油猴脚本开发指南]SweetAlert2的漂亮对话框
https://bbs.tampermonkey.net.cn/thread-1203-1-1.html

[油猴脚本开发指南]SweetAlert2进阶
https://bbs.tampermonkey.net.cn/thread-1303-1-1.html

## 为什么要魔改
油猴脚本属于逆向开发,在原有网站基础上使用脚本管理器及脚本代码对网站原有内容进行修改。
一般JS库都是为网站正向开发准备的,脚本逆向开发过程中许多需要考虑的要点可能是脚本专属要点,正向开发不会涉及,自然正向开发的JS库也不会考虑。
因此脚本逆向开发不可避免的会和网站产生冲突,例如JS冲突、CSS冲突,为了消除这种冲突我们就需要对库代码进行修改优化,本文称之为魔改。
JS冲突由于有脚本沙盒环境,因此我们不必过多考虑。
而CSS冲突就需要我们自己解决。常见的CSS冲突有脚本CSS主动污染网页CSS以及脚本CSS被动被网页全局CSS污染。为了解决这一冲突,我们可以使用ShadowRoot去隔离脚本CSS以及网页CSS。
而sweetalert2并未考虑ShadowRoot,所以我们需要帮助他去考虑。

# 魔改过程

(建议分析sweetalert2源码 不详细展开 有空再说)

***
sweetalert2中提供了一个选项`target="body" //The container element for adding popup into.`
但这并不能支持ShadowRoot,因为在源码中很粗暴的静态定义了多个获取函数均以`document.body`为基点没有回旋余地。
但好消息是因为有这个自定义挂载容器选项,意味着我们需要修改的代码不会很多,至少兼容了一半,我们只需要魔改另一半即可。
既然document.body是静态定义的,那么我们只需要将其修改成动态函数即可
例如
```js
// 定义挂载容器
let target = document.body;
// 修改挂载容器函数
const SetTarget = (newTarget) => (target = newTarget);
// 挂载容器函数
const GetTarget = () => target;
```
创建一个局部变量,默认值为`document.body`,并为这个局部变量创建2个函数Getter/Setter,并将原始JS中静态定义的`document.body`替换成`GetTarget()`
,当需要修改挂载容器时调用`SetTarget()`即可。

***

又由于sweetalert2的默认设置`target="body"`,因此为了减少代码量,我们可以使用`.mixin()`方法,来永久修改默认`target`,这样就不必要每次都单独设置`target`了
```js
Swal = Swal.mixin({ target: container })
```
** container为ShadowRoot内挂载节点变量,由于Swal2代码原因必须为ShadowRoot创建一个子元素并将target挂载在子元素上,不能直接为ShadowRoot,否则会产生冲突
# 魔改代码
```js
// ==UserScript==
// @name      魔改sweetalert2支持shadowRoot
// @namespace   https://scriptcat.org/
// @description 有一说一Swal2的动画还是好看的,其他的就算了
// @version   0.1.0
// @author      DreamNya
// @match       https://bbs.tampermonkey.net.cn/
// @resource    SwalJS   https://cdn.jsdelivr.net/npm/sweetalert2@11.7.20/dist/sweetalert2.js
// @resource    SwalCSShttps://cdn.jsdelivr.net/npm/sweetalert2@11.7.20/dist/sweetalert2.min.css
// @grant       unsafeWindow
// @grant       GM_getResourceText
// @run-at      document-start
// ==/UserScript==

// 定义挂载容器
let target = document.body;
// 修改挂载容器函数
const SetTarget = (newTarget) => (target = newTarget);
// 挂载容器函数
const GetTarget = () => target;

// 读取 @resource SwalJS 并魔改
const SwalJS = GM_getResourceText("SwalJS")
    .replace(
      '"undefined"!=typeof window&&/^ru\\b/.test(navigator.language)&&location.host.match(/\\.(ru|su|by|xn--p1ai)$/)',
      "false"
    ) //去除私货
    .replace(/document\.body/g, "GetTarget()"); //重定义容器

// 通过eval引入魔改后的sweetalert js
eval(SwalJS);

// 读取 @resource SwalCSS 并魔改
const SwalCSS = GM_getResourceText("SwalCSS").replace(/body/g, "");

// 创建shadowRoot节点
const div = document.createElement("div");
const shadowRoot = div.attachShadow({ mode: "open" });
(document.body || document.firstElementChild).append(div);
// shadowRoot添加样式
const style = document.createElement("style");
style.innerText = SwalCSS;
shadowRoot.append(style);
// shadowRoot添加容器
const container = document.createElement("div");
container.classList.add(".container");
shadowRoot.append(container);

// 重定义Swal节点
SetTarget(container);
// 配置默认弹出节点为container
Swal = Swal.mixin({ target: container });

// 暴露变量至网页环境
unsafeWindow.Swal = Swal;

unsafeWindow.onload = () => Swal.fire("完结撒花", "魔改大成功", "success");

```

# 完结撒花
!(data/attachment/forum/202307/24/155058fesnekayfrnsaaka.png)
有一说一Swal2的动画还是好看的,其他的就算了,可惜没找到代替库,将就用了。

王一之 发表于 2023-7-25 09:43:20

CAT_UI也有呀,我觉得挺好看,也可以自己调整

steven026 发表于 2023-7-25 09:48:43

王一之 发表于 2023-7-25 09:43
CAT_UI也有呀,我觉得挺好看,也可以自己调整

Arco没有动画……不好看【
Arco没有预设,全要手动弄,实在太麻烦了
Swal2有弹出动画、关闭动画、图标动画……而且也不大只有60KB,可以作为插件给CAT_UI用,适合我这种懒鬼=-=
页: [1]
查看完整版本: 【油猴脚本开发指南】魔改sweetalert2支持shadowRoot