上一主题 下一主题
ScriptCat,新一代的脚本管理器脚本站,与全世界分享你的用户脚本油猴脚本开发指南教程目录
12下一页
返回列表 发新帖

[油猴脚本开发指南]MutationObserver实战

[复制链接]
  • TA的每日心情
    开心
    2023-2-28 23:59
  • 签到天数: 191 天

    [LV.7]常住居民III

    636

    主题

    5189

    回帖

    6071

    积分

    管理员

    非物质文化遗产社会摇传承人

    积分
    6071

    荣誉开发者管理员油中2周年生态建设者喜迎中秋

    发表于 2021-9-22 11:15:06 | 显示全部楼层 | 阅读模式

    前言

    之前我们实现过b站三联https://bbs.tampermonkey.net.cn/forum.php?mod=viewthread&tid=238

    这节课我们来搞一个高清重置版

    // ==UserScript==
    // @name         重置bilibili三连按钮demo
    // @namespace    https://bbs.tampermonkey.net.cn/
    // @version      0.1
    // @description  给bilibili增加一个真三连按钮
    // @author       Wyz
    // @match        https://www.bilibili.com/video/*
    // @grant        none
    // @run-at       document-end
    // ==/UserScript==
    
    let triple=document.createElement("button");
    triple.innerText="三连";
    triple.style.background="#757575";//颜色弄得差不多吧
    triple.style.color="#fff";
    triple.onclick=function(){
        //三连代码
        let httpRequest = new XMLHttpRequest();
        httpRequest.open('POST', 'https://api.bilibili.com/x/web-interface/archive/like/triple');
        httpRequest.setRequestHeader("Content-type","application/x-www-form-urlencoded");
        httpRequest.withCredentials = true;//设置跨域发送
        let aid=window.__INITIAL_STATE__.aid;
        let sKey="bili_jct";
        let csrf=decodeURIComponent(document.cookie.replace(new RegExp("(?:(?:^|.*;)\\s*" + encodeURIComponent(sKey).replace(/[-.+*]/g, "\\$&") + "\\s*\\=\\s*([^;]*).*$)|^.*$"), "$1")) || null;
        httpRequest.send('aid='+aid+'&csrf='+csrf);
        httpRequest.onreadystatechange = function () {
            if (httpRequest.readyState == 4 && httpRequest.status == 200) {
                var json = JSON.parse(httpRequest.responseText);
                console.log(json);
                if(json.code==0){
                    alert("三连成功!刷新页面可见");
                }else{
                    alert("三连失败/(ㄒoㄒ)/~~");
                }
            }
        };
    };
    let ops=document.querySelector('#arc_toolbar_report .ops');
    //插入三连之后好像会重新生成,不添加就不会重新生成,暂时没弄清什么情况,先这样处理了.
    //主要作用是监听ops的修改,等它修改完成之后再插入我们的三连按钮,另外注意run-at是document-end,要等待ops生成之后再监听,不然query返回null会报错
    //这个事件会多次调用,但是我们insertBefore插入如果元素存在,只是修改而不会新增
    ops.addEventListener("DOMNodeInserted", function(event) {
        let share=document.querySelector('.share');
        share.parentElement.insertBefore(triple,share);
    });
    

    我们需要改的其实很少,改一下addEventListener函数部分就好了,参考

    https://bbs.tampermonkey.net.cn/thread-1007-1-1.html

    可以知道DOMNodeIbserted等价于childList和subtree为true

    所以我们可以大概修改一下代码

    let triple=document.createElement("button");
    triple.innerText="三连";
    triple.style.background="#757575";//颜色弄得差不多吧
    triple.className='checkinsert'
    triple.style.color="#fff";
    triple.onclick=function(){
        //三连代码
        let httpRequest = new XMLHttpRequest();
        httpRequest.open('POST', 'https://api.bilibili.com/x/web-interface/archive/like/triple');
        httpRequest.setRequestHeader("Content-type","application/x-www-form-urlencoded");
        httpRequest.withCredentials = true;//设置跨域发送
        let aid=window.__INITIAL_STATE__.aid;
        let sKey="bili_jct";
        let csrf=decodeURIComponent(document.cookie.replace(new RegExp("(?:(?:^|.*;)\\s*" + encodeURIComponent(sKey).replace(/[-.+*]/g, "\\$&") + "\\s*\\=\\s*([^;]*).*$)|^.*$"), "$1")) || null;
        httpRequest.send('aid='+aid+'&csrf='+csrf);
        httpRequest.onreadystatechange = function () {
            if (httpRequest.readyState == 4 && httpRequest.status == 200) {
                var json = JSON.parse(httpRequest.responseText);
                console.log(json);
                if(json.code==0){
                    alert("三连成功!刷新页面可见");
                }else{
                    alert("三连失败/(ㄒoㄒ)/~~");
                }
            }
        };
    };
    let ops=document.querySelector('#arc_toolbar_report .ops');
    let observerOptions = {
        childList: true, // 观察目标子节点的变化,添加或删除
        attributes: true, // 观察属性变动
    }
    
    function callback(mutationList, observer) {
        if(document.querySelector('.checkinsert')===null){
            let share=document.querySelector('.share');
            share.parentElement.insertBefore(triple,share);
        }
    }
    
    var observer = new MutationObserver(callback);
    observer.observe(ops, observerOptions);

    这里我已经更改成了MutationObserver

    注意,这里有一点小心机

    我新增了一行triple.className='checkinsert'

    并且在callback内首先检查是否不存在document.querySelector('.checkinsert')

    如果不存在才开始插入,如果存在就忽略

    这样写的原因是如果我们不对其进行检查会导致反复插入

    插入又会触发callback回调,从而引发一个死循环的问题。

    所以我对按钮加了一个class叫checkinsert,使querySelector可以方便的找到

    修改后可以发现页面照常出现了按钮

    图片.png

    经过测试功能没有任何问题

    那我们的小实战就结束啦!

    结语

    撒花~

    混的人。
    ------------------------------------------
    進撃!永遠の帝国の破壊虎---李恒道

    入驻了爱发电https://afdian.net/a/lihengdao666
    个人宣言:この世界で私に胜てる人とコードはまだ生まれていません。死ぬのが怖くなければ来てください。
  • TA的每日心情
    开心
    2024-3-13 10:14
  • 签到天数: 211 天

    [LV.7]常住居民III

    293

    主题

    3898

    回帖

    3819

    积分

    管理员

    积分
    3819

    管理员荣誉开发者油中2周年生态建设者喜迎中秋油中3周年挑战者 lv2

    发表于 2021-9-22 14:39:44 | 显示全部楼层
    ggnb!这就更新代码
    上不慕古,下不肖俗。为疏为懒,不敢为狂。为拙为愚,不敢为恶。/ 微信公众号:一之哥哥
    回复

    使用道具 举报

  • TA的每日心情
    奋斗
    2023-6-12 15:07
  • 签到天数: 6 天

    [LV.2]偶尔看看I

    7

    主题

    94

    回帖

    155

    积分

    荣誉开发者

    积分
    155

    荣誉开发者油中2周年生态建设者喜迎中秋

    发表于 2021-9-22 17:49:18 | 显示全部楼层
    ggnb
    不像我,只会偷懒触发页面元素click事件、
    回复

    使用道具 举报

  • TA的每日心情
    开心
    2022-3-7 09:47
  • 签到天数: 1 天

    [LV.1]初来乍到

    22

    主题

    104

    回帖

    170

    积分

    中级工程师

    积分
    170
    发表于 2021-12-21 20:58:45 | 显示全部楼层
    gege,这个是怎么进入到回调中,外面虽然有MutationObserver observe了ops的dom变化,但是添加操作不是在回调里面执行,不是对ops添元素加才了才会触发observe的回调,document.querySelector('.checkinsert')===null用来限制添加一次后触发无限添加这里我能理解(这里用disconnet不加判断是否可以?),但是第一次的回调他是怎么触发的
    回复

    使用道具 举报

  • TA的每日心情
    开心
    2023-2-28 23:59
  • 签到天数: 191 天

    [LV.7]常住居民III

    636

    主题

    5189

    回帖

    6071

    积分

    管理员

    非物质文化遗产社会摇传承人

    积分
    6071

    荣誉开发者管理员油中2周年生态建设者喜迎中秋

    发表于 2021-12-21 21:07:35 | 显示全部楼层
    rubinTime 发表于 2021-12-21 20:58
    gege,这个是怎么进入到回调中,外面虽然有MutationObserver observe了ops的dom变化,但是添加操作不是在回 ...

    啥意思...哥哥,看了几遍我没太理解具体疑似
    混的人。
    ------------------------------------------
    進撃!永遠の帝国の破壊虎---李恒道

    入驻了爱发电https://afdian.net/a/lihengdao666
    个人宣言:この世界で私に胜てる人とコードはまだ生まれていません。死ぬのが怖くなければ来てください。
    回复

    使用道具 举报

  • TA的每日心情
    开心
    2022-3-7 09:47
  • 签到天数: 1 天

    [LV.1]初来乍到

    22

    主题

    104

    回帖

    170

    积分

    中级工程师

    积分
    170
    发表于 2021-12-21 21:15:53 | 显示全部楼层
    李恒道 发表于 2021-12-21 21:07
    啥意思...哥哥,看了几遍我没太理解具体疑似

    就是这个回调是怎么触发的,不是对目标元素有插入才会触发吗,但是那个插入一件三联的元素插入逻辑不是写在回调函数内而不是回调函数外吗
    回复

    使用道具 举报

  • TA的每日心情
    开心
    2023-2-28 23:59
  • 签到天数: 191 天

    [LV.7]常住居民III

    636

    主题

    5189

    回帖

    6071

    积分

    管理员

    非物质文化遗产社会摇传承人

    积分
    6071

    荣誉开发者管理员油中2周年生态建设者喜迎中秋

    发表于 2021-12-21 22:34:19 | 显示全部楼层
    rubinTime 发表于 2021-12-21 21:15
    就是这个回调是怎么触发的,不是对目标元素有插入才会触发吗,但是那个插入一件三联的元素插入逻辑不是写 ...

    let ops=document.querySelector('#arc_toolbar_report .ops');
    let observerOptions = {
        childList: true, // 观察目标子节点的变化,添加或删除
        attributes: true, // 观察属性变动
    }

    function callback(mutationList, observer) {
        if(document.querySelector('.checkinsert')===null){
            let share=document.querySelector('.share');
            share.parentElement.insertBefore(triple,share);
        }
    }

    var observer = new MutationObserver(callback);
    observer.observe(ops, observerOptions);
    混的人。
    ------------------------------------------
    進撃!永遠の帝国の破壊虎---李恒道

    入驻了爱发电https://afdian.net/a/lihengdao666
    个人宣言:この世界で私に胜てる人とコードはまだ生まれていません。死ぬのが怖くなければ来てください。
    回复

    使用道具 举报

  • TA的每日心情
    开心
    2023-2-28 23:59
  • 签到天数: 191 天

    [LV.7]常住居民III

    636

    主题

    5189

    回帖

    6071

    积分

    管理员

    非物质文化遗产社会摇传承人

    积分
    6071

    荣誉开发者管理员油中2周年生态建设者喜迎中秋

    发表于 2021-12-21 22:34:30 | 显示全部楼层
    rubinTime 发表于 2021-12-21 21:15
    就是这个回调是怎么触发的,不是对目标元素有插入才会触发吗,但是那个插入一件三联的元素插入逻辑不是写 ...

        if(document.querySelector('.checkinsert')===null){
            let share=document.querySelector('.share');
            share.parentElement.insertBefore(triple,share);
        }
    是在回调函数内呀
    混的人。
    ------------------------------------------
    進撃!永遠の帝国の破壊虎---李恒道

    入驻了爱发电https://afdian.net/a/lihengdao666
    个人宣言:この世界で私に胜てる人とコードはまだ生まれていません。死ぬのが怖くなければ来てください。
    回复

    使用道具 举报

  • TA的每日心情
    开心
    2023-2-28 23:59
  • 签到天数: 191 天

    [LV.7]常住居民III

    636

    主题

    5189

    回帖

    6071

    积分

    管理员

    非物质文化遗产社会摇传承人

    积分
    6071

    荣誉开发者管理员油中2周年生态建设者喜迎中秋

    发表于 2021-12-21 22:35:03 | 显示全部楼层
    rubinTime 发表于 2021-12-21 21:15
    就是这个回调是怎么触发的,不是对目标元素有插入才会触发吗,但是那个插入一件三联的元素插入逻辑不是写 ...

    大概这个逻辑
    开始监听页面
    插入元素触发回调
    回调检测是否存在对应元素
    不存在则插入,如果存在则不管本次监听
    混的人。
    ------------------------------------------
    進撃!永遠の帝国の破壊虎---李恒道

    入驻了爱发电https://afdian.net/a/lihengdao666
    个人宣言:この世界で私に胜てる人とコードはまだ生まれていません。死ぬのが怖くなければ来てください。
    回复

    使用道具 举报

  • TA的每日心情
    开心
    2022-3-7 09:47
  • 签到天数: 1 天

    [LV.1]初来乍到

    22

    主题

    104

    回帖

    170

    积分

    中级工程师

    积分
    170
    发表于 2021-12-21 22:42:17 | 显示全部楼层
    意思是说最开始的时候是页面加载的时候渲染出ops的子元素触发了第一次的回调,之后是添加一键三连按钮触发回调
    回复

    使用道具 举报

    发表回复

    本版积分规则

    快速回复 返回顶部 返回列表