技术标签: 水印 ECMAScript MutationObserver chrome插件
MutationObserver
接口提供了监视对DOM树所做更改的能力。它被设计为旧的Mutation Events功能的替代品,该功能是DOM3 Events规范的一部分。
简单来讲就是使用Mutationboserver可以监听dom节点的属性变化,节点的删除增加等一系列变化
那MutationObserver有啥用呐?
页面水印的制作
防止水印被用户删除或者改变水印的dom属性
页面弹窗广告无法被删除
对,一些万恶广告就是这么做的
以下MDN摘录,详细请自行查看MDN
disconnect()
阻止 MutationObserver
实例继续接收的通知,直到再次调用其observe()
方法,该观察者对象包含的回调函数都不会再被调用。
observe()
配置
MutationObserver
在DOM更改匹配给定选项时,通过其回调函数开始接收通知。
takeRecords()
从MutationObserver的通知队列中删除所有待处理的通知,并将它们返回到
MutationRecord
对象的新Array
中。
阮一峰大佬已经整理一份关于MutationObserver的详细用法,详情请点击下面链接
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
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("运行错误");
}
});
文章浏览阅读2.8k次。随着互联网的不断发展,网络安全问题越来越受到人们的关注。在2018年全球风险报告中,网络安全问题排名前三,仅次于自然灾害与极端天气事件,这足以说明网络安全的重要性,而防火墙则是网络安全防护的必备产品。“防火墙”这个词可能大家都听过但不是特别熟悉,所以作为国内老牌安全厂商,今天给大家详细普及一下什么是防火墙?1、防火墙基本定义:防火墙指的是一个由软件和硬件设备组合而成、在内部网和外部网之间、专用网与公共网之间的界面上构造的保护屏障.是一种获取安全性方法的形象说法,它是一种计算机硬件和软件的结合,使安全域._路由器防火墙有必要开吗
文章浏览阅读336次。Domino同步到Elasticsearch由于是单线程,时间使用多,能否在原来的基础上进行优化?是的,由于使用java,可以支持多线程,使用多线程来一次性同步测试一下? Domino使用多线程查询数据库在以前文章有介绍过了,以下几张图片简要介绍:已经比没有使用多线程少500秒的时间,大大压缩同步时间。import java.io.BufferedRead..._domino 同步elasticsearch
文章浏览阅读1.3k次。Publisher-Subscriber层:RTPS上的简化抽象Writer-Reader层:对RTPS断点的直接控制相较而言,后者更底层。两个层次的核心角色如下图所示:Publisher-Subscriber层为大多数开发者提供了一个方便的抽象。它允许定义与Topic关联的发布者和订阅者,以及传输Topic数据的简单方法。Writer-Reader层更接近于RTPS标准中定义的概念,并且可以进行更精细的控制,但是要求开发者直接与每个断点的历史记录缓存进行交互。_fastrtsp
文章浏览阅读622次。属性动画> 提供对动画的基本支持,这些动画可以开始,结束并可以添加AnimatorListener。#1.最主要的ObjectAnimator:)1.位移动画(translationX/translationY)2.透明度动画(alpha)3.旋转动画(rotation)#位移动画(translationX/translationY)// 代码实现ObjectAnimator.ofFloat(view, "translationX", 0F, x).setDuration.._android animation.setduration(0)
文章浏览阅读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。
文章浏览阅读728次,点赞26次,收藏26次。介绍了Mybatis的增删改查基础操作,XML映射及动态SQL
文章浏览阅读8.9k次,点赞2次,收藏5次。珊妹子刚入职新公司就接触到apidoc这个生成接口文档的技术,刚刚学习的时候,它本身是有手册的,但是也是就介绍安装和语法,实际遇到的问题,还得自己来抠,这个apidoc是通过后台写接口的注释来达到自动生成接口文档的作用。apidoc是基于node使用的,所以具体安装方法请参考:https://www.cnblogs.com/minsons/articles/7154090.html安装和使..._apidoc 多应用不生效
文章浏览阅读3.3k次,点赞2次,收藏11次。1、引言随着Android系统的不断升级,即时通讯网技术群和社区里的IM和推送开发的程序员们,对于进程保活这件事是越来越悲观,必竟系统对各种保活黑科技的限制越来越多了,想超越系统的挚肘,难度越来越大。但保活这件事就像“激情”之后的余味,总是让人欲罢不能,想放弃又不甘心。那么,除了像上篇《2020年了,Android后台保活还有戏吗?看我如何优雅的实现!》这样的正经白名单方式,不正经的“黑科..._tim安卓版有32位版本吗
文章浏览阅读906次。机房收费系统中需要组合查询,如图 首先,我分析了查询的流程,并画了流程图,如下:思路总结好了,下面就是代码的编写了(以学生上机统计信息为例):Dim mrc As ADODB.RecordsetDim txtSQL As StringDim Msgtxt As String If Combo3(0).Text = "" Then_vb 组合查询
文章浏览阅读4.2k次,点赞8次,收藏49次。搭建WEB工程需要很多配置操作,这里应有尽有!_写一个springweb项目需要搭建哪些
文章浏览阅读855次,点赞16次,收藏15次。并且实现OLED显示当前的功能名称。
文章浏览阅读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获取启动图标