我正在参加「创意开发 投稿大赛」详情请看:掘金创意开发大赛来了!
感觉创意大赛的奖品很丰富,想要多写几篇,发现是真的难~
这次的话,尝试着编写了一个非常简易的一键三连的油猴的脚本文件,内容对于前端开发者来说,应该算是入门级的吧~,对我来说,就没有那么那么容易~,大佬们,轻喷...
前言
我对于油猴插件一直是处于一种非常好奇的状态,感觉别人写出来的插件都特别特别厉害,最典型的就是读书的时候,写个插件,自动刷学习视频等等,网上还有去除广告、统计信息、导出文档、下载视频等等插件,很多很多。
从来没有接触过,就觉得它是一件学习成本非常高的事情,像是洪水猛兽一般,导致一直是搁置的状态,然后这次有活动,我就想到了这个点,就想要试一试。
接触下来后,其实写这样的脚本文件,远没有我想象的那么难,如果是对于前端来说,更没有啥学习成本,因为全部都是js代码~
一、实现效果
点击创建出来的一键三连的按钮,就可以实现点赞、关注、评论,下次看文不迷路~
实现逻辑倒是非常简单,但因为我对于前端没有那么熟悉,所以其中的一些坑,我都给躺了一遍~
二、完整代码
就是写着玩,完整代码如下:
// ==UserScript==
// @name 掘金牌一键三连
// @namespace https://juejin.cn/user/2859142558267559
// @version 0.0.1
// @description 打开博文,点击一键点赞评论按钮后自动为该博文点赞收藏关注,前提是已经登录 掘金
// @author 宁在春
// @include *://juejin.cn/post/*
// @include *.juejin.cn/post/*
// ==/UserScript==
(function() {
'use strict';
var tag_list = document.createElement("div");
tag_list.classList.add('tag-list'); // 给创建出来的div 添加一个 class
var button = document.createElement("button"); //创建一个按钮
button.textContent = "一键三连"; //按钮内容
button.style.width = "90px"; //按钮宽度
button.style.height = "32px"; //按钮高度
button.style.align = "center"; //文本居中
button.style.color = "white"; //按钮文字颜色
button.style.background = "#e33e33"; //按钮底色
button.style.border = "1px solid #e33e33"; //边框属性
button.style.borderRadius = "4px"; //按钮四个角弧度
button.style.fontSize="14px";
tag_list.appendChild(button);
/**
* ele: html 元素
* className (string): 要判断的类名
* 返回值: 元素含有该类名返回 true,不包含返回 false
*/
function hasClass (ele, className) {
var reg = new RegExp('(^|\s)' + className + '(\s|$)')
return reg.test(ele.className)
}
// 点赞
function clickLike(){
var likeElement=document.getElementsByClassName("panel-btn")[0];
if(!hasClass(likeElement,"active")){
likeElement.click(); //点赞
}
}
// 关注
function clickFollow(){
var followElement=document.getElementsByClassName("follow-button")[0];
if(!hasClass(followElement,"followed")){
followElement.click(); //点击关注
}
}
//获取焦点
function getFocus() {
document.getElementsByClassName('rich-input')[0].focus();
};
//打开emoje 表情选择框
function getEmojeClick() {
document.getElementsByClassName("emoji-box")[0].click();
}
var items = [112,102,92,80,71,66,61,55,31,28];
var randomNumber=items[Math.floor(Math.random() * items.length)];
//选择emoje表情
function getChoseEmoje() {
setTimeout(function(){
var emojipicker = document.getElementsByClassName("emojipicker")[0];
var emojeImage = emojipicker.getElementsByClassName("list")[1];
var item = emojeImage.getElementsByClassName("item")[randomNumber];
var img = item.getElementsByClassName("image")[0];
img.click();
},100);// setTimeout 0.1秒后执行
}
//发表评论
function submitComment(){
setTimeout(function(){
document.getElementsByClassName("submit-btn")[0].click(); //发表评论
},100);// setTimeout 0.1秒后执行
}
function clickBotton(){
setTimeout(function(){
//调用点赞方法
clickLike()
//调用关注方法
clickFollow()
//让评论框获取焦点
getFocus()
//打开emoje 表情选择框
getEmojeClick()
//选择emoje表情
getChoseEmoje()
//发表评论
submitComment();
},100);// setTimeout 0.1秒后执行
}
// 延时将元素添加到页面上,有时候页面加载缓慢,会导致,脚本执行完,但页面还加载完~,导致出现错误
setTimeout(function(){
var like_comment = document.getElementsByClassName('tag-list-box'); //getElementsByClassName 返回的是数组,所以要用[] 下标
like_comment[0].appendChild(tag_list); //把按钮加入到 x 的子节点中
},500);
button.addEventListener("click", clickBotton) //监听按钮点击事件
})(); //(function(){})() 表示该函数立即执行
// ==UserScript==
// @name 掘金牌一键三连
// @namespace https://juejin.cn/user/2859142558267559
// @version 0.0.1
// @description 打开博文,点击一键点赞评论按钮后自动为该博文点赞收藏关注,前提是已经登录 掘金
// @author 宁在春
// @include *://juejin.cn/post/*
// @include *.juejin.cn/post/*
关于这些 @
的标识,大家可以直接去搜一下油猴脚本的开发文档即可,当然这几个也很简单,就不多言了,如果感兴趣了,可以去找一找~
将这个脚本添加到油猴的脚本管理页面即可:
(图片说明:点击添加新脚本)
(图片说明:把上面的代码,直接复制粘贴过来,Ctrl+S 保存就可以生效了)
三、踩坑
踩坑全部集中在添加评论那一步。
我原本的想法是
- 构建好一个评论内容的数组
- 每次一键三连时,从中随机抽取一条评论
然后踩坑之旅就开始啦~
我们先来看看掘金的评论框区域的代码:
<div data-v-013fc4fa="" data-v-7c7c7498="" class="input-box">
<div contenteditable="true" spellcheck="false"
placeholder="输入评论(Enter换行,Ctrl + Enter发送)"
disabled="disabled" class="rich-input empty">
</div>
</div>
这里的话,它是用用了一个 contenteditable
来做为输入框的。
我总共是采取了三种方式去尝试实现~
3.1、直接改变 div 的内容
我的想法是,先触发div的点击方法,然后就会出现光标在输入框内,这样就可以改变其中内容啦。
第一次就这么开始啦~
var comment=["针不戳呀,写的针不戳!","学习了!b( ̄▽ ̄)d","本文不错( ̄ˇ ̄),值得学习!(= ̄ω ̄=)","感谢博主的分享!(^ ^)/▽▽\(^ ^)","感谢博主,你的文章让我得到一些收获!( ̄ˇ ̄)"];
var STARTNUMBER = -1;
var ENDNUMBER = 5;
var temp_count = Math.floor(Math.random()*(STARTNUMBER-ENDNUMBER+1))+ENDNUMBER
document.getElementsByClassName("input-box")[0].click(); //打开评论区
document.getElementsByClassName('rich-input')[0].classList.remove("empty");
document.getElementsByClassName("rich-input")[0].innerHTML = comment[temp_count]; //随机把一条预先写好的评论赋值到评论框里面
document.getElementsByClassName("submit-btn")[0].click(); //发表评论
结果:
内容上去是上去了,但实际点击方法根本没有被触发,这只能说是改变了div的展示内容。
完全没有牵扯到值的变化,在这一步的时候,我又接着这个思路,将底下的按钮的禁用给去掉,手动提交了一遍,果然报评论为空的错误~
咋说勒,对这个想法不死心,我以为是我没有成功触发点击方法的问题,又接着去搜索怎么触发div的点击方法,搜了蛮多的,但是都不太对。
其中还有尝试了下面的这样一个方法:
function emulateMouseClick (element) {
// 创建事件
var event = document.createEvent('MouseEvents')
// 定义事件 参数: type, bubbles:事件冒泡, cancelable :是否可撤销
event.initEvent('click', true, true)
// 触发对象可以是任何元素或其他事件目标
element.dispatchEvent(event)
}
参考链接:
这个时候,我又想到了 div 身上的 contenteditable="true"
属性,对这个属性有点陌生,感觉它应该对我有点帮助,然后就搜到了下面的内容:
(图片说明:链接)
看到可编辑之后这几个字后,我又往这个方向继续去探索~
又继续搜到以下的内容:
contenteditable联合v-html实现数据双向绑定的vue组件
找着找着,我就去找如何让div触发焦点事件了,就又有了下面的尝试
3.2、尝试让div获取焦点
在这一步的时候,率先尝试的是下面的这个方法,因为这位作者尝试用Vue去实现掘金发送沸点的那个选择表情的功能,这篇文章也让我避免了一些坑~
Vue实现可编辑div获取焦点🎉 感谢作者~
这篇文章用了
以上三种方式来尝试实现,第一个 tabindex=0
,好像是不行的,我就直接跳过了。
我又接着去找寻第二种方式:
发现下面这篇文章:
在可编辑div中定位光标和设置光标,还给了代码和实例
作者的源代码,大家可以点链接过去看一下,我就不增加篇幅了。
参考完上面作者的代码,我改成了下面这样:
// 定义最后光标对象
var lastEditRange;
// 编辑框点击事件
document.getElementsByClassName("rich-input")[0].onclick = function () {
// 获取选定对象
var selection = getSelection();
// 设置最后光标对象
lastEditRange = selection.getRangeAt(0);
};
// 编辑框按键弹起事件
document.getElementsByClassName("rich-input")[0].onkeyup = function () {
// 获取选定对象
var selection = getSelection();
// 设置最后光标对象
lastEditRange = selection.getRangeAt(0);
};
// 发送表情
function editContent() {
// 获取可编辑框
var editor = document.getElementsByClassName("rich-input")[0];
// 获取输入框
var comment = [
"针不戳呀,写的针不戳!",
"学习了!b( ̄▽ ̄)d",
"本文不错( ̄ˇ ̄),值得学习!(= ̄ω ̄=)",
"感谢博主的分享!(^ ^)/▽▽\(^ ^)",
"感谢博主,你的文章让我得到一些收获!( ̄ˇ ̄)",
];
var STARTNUMBER = -1;
var ENDNUMBER = 5;
var temp_count =
Math.floor(Math.random() * (STARTNUMBER - ENDNUMBER + 1)) + ENDNUMBER; //取STARTNUMBER-ENDNUMBER之间的随机数 [STARTNUMBER,ENDNUMBER]
var editComment = comment[temp_count];
// 编辑框获得焦点
editor.focus();
// 获取选定对象
var selection = getSelection();
// 如果保存的有上次的光标对象
if (lastEditRange) {
// 清除所有选区
selection.removeAllRanges();
// 添加最后光标还原之前的状态
selection.addRange(lastEditRange);
}
// 如果选定对象范围是编辑框范围
// 创建表情文本节点进行插入
var emojiText = document.createTextNode(editComment);
// 如果文本框的子元素大于0,则表示有其他元素,则按照位置插入表情节点
if (editor.childNodes.length > 0) {
for (var i = 0; i < editor.childNodes.length; i++) {
if (i == selection.anchorOffset) {
editor.insertBefore(emojiText, editor.childNodes[i]);
}
}
} else {
// 否则直接插入一个表情元素
editor.appendChild(emojiText);
}
// 创建新的光标对象
var range = document.createRange();
// 将光标对象的范围界定为新建的表情节点
range.selectNodeContents(emojiText);
// 定位光标位置在表情节点的最大长度位置
range.setStart(emojiText, emojiText.length);
// 将选区折叠为一个光标
range.collapse(true);
// 清除所有光标对象A
selection.removeAllRanges();
// 添加新的光标对象
selection.addRange(range);
// 记录最后光标对象
lastEditRange = selection.getRangeAt(0);
}
editContent();
执行效果如下:
(图片说明:focus 方法确实成功,点亮文本框了,但是内容的改变,仍然无法被监听)
包括光标的位置此时也改变了,但它仍然只算我修改了视图内容,因为评论的按钮仍然是暗的。
这个时候,我突然想到,我手动给它赋值不行,我可以操纵复制粘贴方法啊。
然后这个时候又开始踩坑了!!!
3.3、尝试使用copy、paste 方法
我只是隐约知道浏览器可以操作 copy、paste 方法,并没有尝试过。
又继续开始疯狂的搜索~
这次尝试之前,我先了试,确定在获取到div内容框的焦点后,cv是可以的之后,我就想在粘贴板上做点文章
我的思路是,先把评论内容制造好,然后用Js复制到粘贴版上,之后再调用 paste 方法将内容粘贴到 div 的那个内容框中去。
想法还是挺美好的。
参考着下面的内容进行着尝试
尝试了下面的内容
Document.execCommand()
方法- 异步的 Clipboard API
copy
事件和paste
事件
3.3.1、Document.execCommand()
最先尝试的 Document.execCommand()
事件,Chrome、Firefox 不支持
document.execCommand('paste')
导致这一步直接GG 了~
3.3.2、Clipboard API
然后 Clipboard API
是偏向于操作剪贴板,就是可以写入,读出,但是并不是触发 paste方法,
我也找了一个小案例:
<body>
<p>hello world</p>
<button id="btn-copy">copy text</button>
<button id="btn-paste">copy text</button>
<script>
const copyBtn = document.querySelector('#btn-copy');
copyBtn.addEventListener(
'click',
async function () {
try {
// 将 p 标签内的文本编辑后写入剪切板
await navigator.clipboard.writeText(
`${document.querySelector('p').textContent}!`
);
} catch (e) {
console.error('Failed to copy: ', e);
}
},
false
);
const pasteBtn = document.querySelector('#btn-paste');
pasteBtn.addEventListener(
'click',
async function () {
try {
// 读取剪切板中的文本
const text = await navigator.clipboard.readText();
console.log(text);
} catch (e) {
console.error('Failed to read clipboard content: ', e);
}
},
false
);
</script>
</body>
这个坑也让我躺了,又去尝试下一个方法:
另外还参考了 MDN 文档:Element: paste event
3.3.3、clipboard.js
我也尝试了这个方法,但是我发现,它只能监听用户的 paste 事件,当时粘贴事件发生时,才能处理事件~
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script
type="text/javascript"
src="https://cdn.bootcss.com/clipboard.js/2.0.1/clipboard.js"
></script>
</head>
<body>
<!-- Target -->
<input id="input" value="你好,我是宁在春" />
<!-- Trigger -->
<button class="copy">Copy to clipboard</button>
<button id="paste">paste</button>
<textarea id="output"> </textarea>
<script>
var clipboard = new ClipboardJS(".copy", {
text: function (trigger) {
return doCopy();
},
});
var pastBtn = document.getElementById("output");
pastBtn.addEventListener("paste", function (event) {
console.log(pastBtn);
if (event.clipboardData || event.originalEvent) {
var clipboardData = event.clipboardData || window.clipboardData;
var val = clipboardData.getData("text");
if (val) {
doPast(val);
} else {
throw new Error("剪切板内容为空或者非文本类型");
}
event.preventDefault();
}
});
//获取要复制到剪切板的内容
function doCopy() {
return document.getElementById("input").value;
}
//处理剪切板内容
function doPast(value) {
alert(value);
}
</script>
</body>
</html>
补充
:在这一步的时候,我原本还想继续去尝试用JavaScript 模拟键盘按键去尝试触发复制粘贴事件的,但是没有去尝试啦。
主要在网上搜到的知识,不敢说千篇一律,但是点开大都数文章略微有点让人失望~,还有一点就是转载来转载去(个人吐槽,轻喷),一篇文章能看见好几次,哈哈
后来想起第一篇看到的文章,聊到的掘金的表情,然后就有了下面的灵光一现~
3.4、灵光一现
这个时候,我想着,手动改你内容不行,直接点击表情,然后放到div中,这样总可以吧
因为只要打开emjoe 面板,点一下表情,就会自动的添加到那个内容框中去,这一次尝试竟然是ok的~
// 获取评论区焦点
function getFocus() {
document.getElementsByClassName("rich-input")[0].focus();
}
//打开emoje 表情选择框
function getEmojeClick() {
document.getElementsByClassName("emoji-box")[0].click();
}
//选择emoje表情
function getChoseEmoje() {
var emojipicker = document.getElementsByClassName("emojipicker")[0];
var emojeImage = emojipicker.getElementsByClassName("list")[1];
var item = emojeImage.getElementsByClassName("item")[0];
var spanImage = item.getElementsByClassName("byte-tooltip__wrapper")[0];
var img = spanImage.querySelector("img");
img.click();
}
getFocus();
getEmojeClick();
getChoseEmoje();
大家可以直接把这一步放到掘金的文章页面,F12后,将代码复制运行,是可行的~
在这一步可行之后,我又想在这个基础上增强,既然选择掘金的表情是可以的,那我先加一个表情,然后再加上我自己的评论内容,不就行了吗~,说做就做
代码不贴了,直接说结果,失败了~,还是和之前一模一样,发起请求的时候,根本没有携带这个数据内容
后记
最后实现效果大家也看到了,因为暂时没有找到更好的一个方法去实现,我最后用表情妥协了,表情选用了几个比较正向的表情,然后在评论的时候进行随机选择
代码比较偏向于基础,使用到的全部都是 JavaScript 中的基础知识,写出来也是我的一个尝试,大家喜欢与否,我并不能知晓。
关于这篇文章,如果是对于从来没有接触过油猴脚本的小伙伴来说,拿来看一看这么简单,也算是一个接触。
除了这一点外,我也不敢说此文,对读者而言,能有多少收获了。
如果感兴趣可以多做尝试,在这其中,多少会有一些收获。
对了,不要被陌生的事物吓住了,只要迈出第一步,那么就是成功的😀
我明白我对于前端领域,就是一位小白,文中不可避免可能会出现错误或遗漏,请大家一定要进行指出!
当然如果让你有所收获的话,也希望可以留下你的赞~,哈哈
大伙可以直接将代码放进油猴,来试一试这个脚本的效果,可以在这篇文章中,让我看到你们的实操吗~
也可以把第一个中 function 中的所有代码直接放到网页中执行,也是可以的~
写于 2022 年 8 月 7 日晚,作者:宁在春