MutationObserver-程序员宅基地

技术标签: 水印  ECMAScript  MutationObserver  chrome插件  

MutationObserver

MutationObserver接口提供了监视对DOM树所做更改的能力。它被设计为旧的Mutation Events功能的替代品,该功能是DOM3 Events规范的一部分。

简单来讲就是使用Mutationboserver可以监听dom节点的属性变化,节点的删除增加等一系列变化

那MutationObserver有啥用呐?

  1. 页面水印的制作

    防止水印被用户删除或者改变水印的dom属性

  2. 页面弹窗广告无法被删除

    对,一些万恶广告就是这么做的

具体用法

以下MDN摘录,详细请自行查看MDN

  1. disconnect()

    阻止 MutationObserver 实例继续接收的通知,直到再次调用其observe()方法,该观察者对象包含的回调函数都不会再被调用。

  2. observe()

    配置MutationObserver在DOM更改匹配给定选项时,通过其回调函数开始接收通知。

  3. takeRecords()

    从MutationObserver的通知队列中删除所有待处理的通知,并将它们返回到MutationRecord对象的新Array中。

阮一峰大佬已经整理一份关于MutationObserver的详细用法,详情请点击下面链接

阮一峰 MutationObserver详解

阮一峰 MutationObserver详解 新版博客

配合canvas实现无法隐藏和删除的水印

var WaterMark = function() {
    
    this.container = null
    this.defaultOption = {
    
        id: 'watermark-global',
        width: 300,
        preventTamper: true,
        height: 115,
        text: 'icc-watermark',
        font: '20px Times New Roman',
        rotateDegree: (30 * Math.PI) / 180,
        style: {
    
            'pointer-events': 'none',
            width: '100%',
            height: '100%',
            top: 0,
            left: 0,
            position: 'fixed',
            'z-index': 5000
        }
    }
    this.canAdd = true
}

//create image base64 url via canvas
WaterMark.prototype.createImageUrl = function(options) {
    
    var canvas = document.createElement('canvas')
    var text = options.text
    canvas.width = 300
    canvas.height = 150
    var ctx = canvas.getContext('2d')
    ctx.rotate((19 * Math.PI) / 180)
    ctx.font = '15px Microsoft JhengHei'
    ctx.fillStyle = 'rgba(17, 17, 17, 0.10)'
    ctx.textAlign = 'left'
    ctx.textBaseline = 'Middle'
    ctx.fillText(text, canvas.width / 3, canvas.height / 2)
    ctx.rotate(options.rotateDegree)
    return canvas.toDataURL('image/png')
}

WaterMark.prototype.createContainer = function(options, forceCreate) {
    
    var oldDiv = document.getElementById(options.id)
    var existsOldDiv = typeof oldDiv != 'undefined' && oldDiv != null
    if (!forceCreate && existsOldDiv) {
    
        return existsOldDiv
    }
    var url = this.createImageUrl(options)
    var div = existsOldDiv ? oldDiv : document.createElement('div')
    div.id = options.id

    var parentEl = options.preventTamper
        ? document.body
        : options.parentEl || document.body

    if (typeof parentEl === 'string') {
    
        if (parentEl.startsWith('#')) {
    
            parentEl = parentEl.substring(1)
        }
        parentEl = document.getElementById(parentEl)
    }
    var rect = parentEl.getBoundingClientRect()
    options.style.left = (options.left || rect.left) + 'px'
    options.style.top = (options.top || rect.top) + 'px'
    div.style.cssText = this.getStyleText(options)
    div.setAttribute('class', '')
    div.style.background = 'url(' + url + ') repeat top left'
    !oldDiv && parentEl.appendChild(div)
    return div
}

WaterMark.prototype.getStyleText = function(options) {
    
    var ret = '',
        style = options.style
    Object.keys(style).forEach(function(k) {
    
        ret += k + ': ' + style[k] + ';'
    })
    return ret
}

WaterMark.prototype.observe = function(options) {
    
    var self = this
    self.container = self.createContainer(options, true)
    var target = self.container
    var observer = new MutationObserver(function() {
    
        observer.disconnect()
        if (self.canAdd) {
    
            self.container = self.createContainer(options, true)
        }
        var config = {
    
            attributes: true,
            childList: true,
            characterData: true,
            subtree: true
        }
        observer.observe(target, config)
    })
    var config = {
    
        attributes: true,
        childList: true,
        characterData: true,
        subtree: true
    }
    observer.observe(target, config)
}

WaterMark.prototype.observeBody = function(options) {
    
    //observe body element, recreate if the element is deleted
    var self = this
    var pObserver = new MutationObserver(function(mutations) {
    
        mutations.forEach(function(m) {
    
            var length = m.removedNodes.length
            if (m.type === 'childList' && length > 0) {
    
                var watermarkNodeRemoved = false
                for (var n = 0; n < length; n++) {
    
                    if (m.removedNodes[n].id === options.id) {
    
                        watermarkNodeRemoved = true
                        break
                    }
                }
                if (watermarkNodeRemoved) {
    
                    self.observe(options, true)
                }
            }
        })
    })
    pObserver.observe(document.body, {
     childList: true, subtree: true })
}

//初始化水印
WaterMark.prototype.init = function(options) {
    
    options = !options
        ? this.defaultOption
        : Object.assign({
    }, this.defaultOption, options)
    this.observe(options)
    this.observeBody(options)
}

//暂时隐藏水印
WaterMark.prototype.destory = function(options) {
    
    options = !options
        ? this.defaultOption
        : Object.assign({
    }, this.defaultOption, options)
    var target = document.getElementById(options.id)
    if (target) {
    
        this.canAdd = false
        target.style.display = 'none'
    }
}

export default WaterMark

chrome插件配合MutationObserver实现广告去除

window.addEventListener("DOMContentLoaded", () => {
    
  try {
    
    console.log("插件start");
    var Observer = new MutationObserver(function(mutations) {
    
      mutations.forEach(function(m) {
    
        var length = m.addedNodes.length;
        if (m.type === "childList" && length > 0) {
    
          for (var n = 0; n < length; n++) {
    
            if (m.addedNodes[n].className === 'advertModal') {
    
                    let modal = document.querySelectorAll(".advertModal")[0];
                    let dom = document.querySelectorAll(".advertModal-content")[0];
                    if(modal&&dom){
    
                        dom.remove();
                        modal.style.position = "unset";
                        console.log("插件success");
                    }
              break;
            }
          }
        }
      });
    });
    Observer.observe(document.body, {
     childList: true, subtree: true });
  } catch (err) {
    
    console.log("运行错误");
  }
});

chrome插件攻略

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/mengshang529/article/details/99349187

智能推荐

什么是防火墙?服务器防火墙建议开启吗?_路由器防火墙有必要开吗-程序员宅基地

文章浏览阅读2.8k次。随着互联网的不断发展,网络安全问题越来越受到人们的关注。在2018年全球风险报告中,网络安全问题排名前三,仅次于自然灾害与极端天气事件,这足以说明网络安全的重要性,而防火墙则是网络安全防护的必备产品。“防火墙”这个词可能大家都听过但不是特别熟悉,所以作为国内老牌安全厂商,今天给大家详细普及一下什么是防火墙?1、防火墙基本定义:防火墙指的是一个由软件和硬件设备组合而成、在内部网和外部网之间、专用网与公共网之间的界面上构造的保护屏障.是一种获取安全性方法的形象说法,它是一种计算机硬件和软件的结合,使安全域._路由器防火墙有必要开吗

Domino毫秒级查询利器Elasticsearch(二)_domino 同步elasticsearch-程序员宅基地

文章浏览阅读336次。Domino同步到Elasticsearch由于是单线程,时间使用多,能否在原来的基础上进行优化?是的,由于使用java,可以支持多线程,使用多线程来一次性同步测试一下? Domino使用多线程查询数据库在以前文章有介绍过了,以下几张图片简要介绍:已经比没有使用多线程少500秒的时间,大大压缩同步时间。import java.io.BufferedRead..._domino 同步elasticsearch

Apollo:DDS与Fast-RTPS_fastrtsp-程序员宅基地

文章浏览阅读1.3k次。Publisher-Subscriber层:RTPS上的简化抽象Writer-Reader层:对RTPS断点的直接控制相较而言,后者更底层。两个层次的核心角色如下图所示:Publisher-Subscriber层为大多数开发者提供了一个方便的抽象。它允许定义与Topic关联的发布者和订阅者,以及传输Topic数据的简单方法。Writer-Reader层更接近于RTPS标准中定义的概念,并且可以进行更精细的控制,但是要求开发者直接与每个断点的历史记录缓存进行交互。_fastrtsp

Android 属性动画(Animator)简单使用_android animation.setduration(0)-程序员宅基地

文章浏览阅读622次。属性动画> 提供对动画的基本支持,这些动画可以开始,结束并可以添加AnimatorListener。#1.最主要的ObjectAnimator:)1.位移动画(translationX/translationY)2.透明度动画(alpha)3.旋转动画(rotation)#位移动画(translationX/translationY)// 代码实现ObjectAnimator.ofFloat(view, "translationX", 0F, x).setDuration.._android animation.setduration(0)

最新金九银十JAVA面试合集:Java+Spring+MySQL+Redis+算法-程序员宅基地

文章浏览阅读502次,点赞8次,收藏20次。一次偶然,从朋友那里得到一份“java高分面试指南”,里面涵盖了25个分类的面试题以及详细的解析:JavaOOP、Java集合/泛型、Java中的IO与NIO、Java反射、Java序列化、Java注解、多线程&并发、JVM、Mysql、Redis、Memcached、MongoDB、Spring、Spring Boot、Spring Cloud、RabbitMQ、Dubbo 、MyBatis 、ZooKeeper 、数据结构、算法、Elasticsearch 、Kafka 、微服务、Linux。

Mybatis-程序员宅基地

文章浏览阅读728次,点赞26次,收藏26次。介绍了Mybatis的增删改查基础操作,XML映射及动态SQL

随便推点

apidoc的使用和常见问题的解决办法_apidoc 多应用不生效-程序员宅基地

文章浏览阅读8.9k次,点赞2次,收藏5次。珊妹子刚入职新公司就接触到apidoc这个生成接口文档的技术,刚刚学习的时候,它本身是有手册的,但是也是就介绍安装和语法,实际遇到的问题,还得自己来抠,这个apidoc是通过后台写接口的注释来达到自动生成接口文档的作用。apidoc是基于node使用的,所以具体安装方法请参考:https://www.cnblogs.com/minsons/articles/7154090.html安装和使..._apidoc 多应用不生效

史上最强Android保活思路:深入剖析腾讯TIM的进程永生技术_tim安卓版有32位版本吗-程序员宅基地

文章浏览阅读3.3k次,点赞2次,收藏11次。1、引言随着Android系统的不断升级,即时通讯网技术群和社区里的IM和推送开发的程序员们,对于进程保活这件事是越来越悲观,必竟系统对各种保活黑科技的限制越来越多了,想超越系统的挚肘,难度越来越大。但保活这件事就像“激情”之后的余味,总是让人欲罢不能,想放弃又不甘心。那么,除了像上篇《2020年了,Android后台保活还有戏吗?看我如何优雅的实现!》这样的正经白名单方式,不正经的“黑科..._tim安卓版有32位版本吗

组合查询 ——vb_vb 组合查询-程序员宅基地

文章浏览阅读906次。机房收费系统中需要组合查询,如图 首先,我分析了查询的流程,并画了流程图,如下:思路总结好了,下面就是代码的编写了(以学生上机统计信息为例):Dim mrc As ADODB.RecordsetDim txtSQL As StringDim Msgtxt As String If Combo3(0).Text = "" Then_vb 组合查询

构建一个Web工程所需的环境搭建(详细图文)_写一个springweb项目需要搭建哪些-程序员宅基地

文章浏览阅读4.2k次,点赞8次,收藏49次。搭建WEB工程需要很多配置操作,这里应有尽有!_写一个springweb项目需要搭建哪些

基于51单片机的智能小车-程序员宅基地

文章浏览阅读855次,点赞16次,收藏15次。并且实现OLED显示当前的功能名称。

Flutter App图标、启动图的生成 和 修改 App 名称_flutter获取启动图标-程序员宅基地

文章浏览阅读1.1k次,点赞2次,收藏3次。App图标生成规格说明:android:https://developer.android.google.cn/google-play/resources/icon-design-specificationsios:https://developer.apple.com/design/human-interface-guidelines/ios/icons-and-images/app-icon/图标尺寸:android:512 x 512ios:1024 x 1024插件:_flutter获取启动图标