[英]Is there any recaptcha v2 close event?
我正在用这样的代码渲染 grecaptcha
let callback;
const p = new Promise((resolve) => callback = (result) => resolve(result));
grecaptcha.render(el, {
sitekey: window.settings.recaptchaKey,
size: "invisible",
type: "image",
callback: result => callback(result),
badge: "inline"
});
const key = await p;
一切正常,但是如果用户单击recaptcha modal 的背景,recaptcha 将关闭并且我无法检测到它,所以我等待无限响应
我需要某种事件或回调来检测它何时关闭
不幸的是,Google 没有 API 事件来跟踪这一点,但我们可以使用Mutation Observer Web API 自己跟踪 Google API 的 DOM 更改。
我们在这里有两个挑战。
1)检测挑战何时显示并获取挑战的覆盖div
function detectWhenReCaptchaChallengeIsShown() {
return new Promise(function(resolve) {
const targetElement = document.body;
const observerConfig = {
childList: true,
attributes: false,
attributeOldValue: false,
characterData: false,
characterDataOldValue: false,
subtree: false
};
function DOMChangeCallbackFunction(mutationRecords) {
mutationRecords.forEach((mutationRecord) => {
if (mutationRecord.addedNodes.length) {
var reCaptchaParentContainer = mutationRecord.addedNodes[0];
var reCaptchaIframe = reCaptchaParentContainer.querySelectorAll('iframe[title*="recaptcha"]');
if (reCaptchaIframe.length) {
var reCaptchaChallengeOverlayDiv = reCaptchaParentContainer.firstChild;
if (reCaptchaChallengeOverlayDiv.length) {
reCaptchaObserver.disconnect();
resolve(reCaptchaChallengeOverlayDiv);
}
}
}
});
}
const reCaptchaObserver = new MutationObserver(DOMChangeCallbackFunction);
reCaptchaObserver.observe(targetElement, observerConfig);
});
}
首先,我们创建了一个目标元素,用于观察 Google iframe 的外观。 我们将 document.body 定位为 iframe 将附加到它:
const targetElement = document.body;
然后我们为 MutationObserver 创建了一个配置对象。 在这里,我们可以指定我们在 DOM 更改中跟踪的确切内容。 请注意,默认情况下所有值都是“false”,因此我们只能留下“childList”——这意味着我们只会观察目标元素的子节点更改——在我们的例子中是 document.body:
const observerConfig = {
childList: true,
attributes: false,
attributeOldValue: false,
characterData: false,
characterDataOldValue: false,
subtree: false
};
然后我们创建了一个函数,当观察者检测到我们在配置对象中指定的特定类型的 DOM 更改时将调用该函数。 第一个参数表示一个Mutation Observer对象数组。 我们抓住了覆盖 div 并返回了 Promise。
function DOMChangeCallbackFunction(mutationRecords) {
mutationRecords.forEach((mutationRecord) => {
if (mutationRecord.addedNodes.length) { //check only when notes were added to DOM
var reCaptchaParentContainer = mutationRecord.addedNodes[0];
var reCaptchaIframe = reCaptchaParentContainer.querySelectorAll('iframe[title*="recaptcha"]');
if (reCaptchaIframe.length) { // Google reCaptcha iframe was loaded
var reCaptchaChallengeOverlayDiv = reCaptchaParentContainer.firstChild;
if (reCaptchaChallengeOverlayDiv.length) {
reCaptchaObserver.disconnect(); // We don't want to observe more DOM changes for better performance
resolve(reCaptchaChallengeOverlayDiv); // Returning the overlay div to detect close events
}
}
}
});
}
最后我们实例化了一个观察者本身并开始观察 DOM 变化:
const reCaptchaObserver = new MutationObserver(DOMChangeCallbackFunction);
reCaptchaObserver.observe(targetElement, observerConfig);
2)第二个挑战是那个帖子的主要问题——我们如何检测到挑战已经结束? 好吧,我们再次需要 MutationObserver 的帮助。
detectReCaptchaChallengeAppearance().then(function (reCaptchaChallengeOverlayDiv) {
var reCaptchaChallengeClosureObserver = new MutationObserver(function () {
if ((reCaptchaChallengeOverlayDiv.style.visibility === 'hidden') && !grecaptcha.getResponse()) {
// TADA!! Do something here as the challenge was either closed by hitting outside of an overlay div OR by pressing ESC key
reCaptchaChallengeClosureObserver.disconnect();
}
});
reCaptchaChallengeClosureObserver.observe(reCaptchaChallengeOverlayDiv, {
attributes: true,
attributeFilter: ['style']
});
});
所以我们所做的是我们使用我们在 Step1 中创建的 Promise 获得了 Google reCaptcha 挑战覆盖 div,然后我们订阅了覆盖 div 上的“样式”更改。 这是因为当挑战结束时 - 谷歌将其淡出。 需要注意的是,当一个人成功解决验证码时,可见性也会被隐藏。 这就是我们添加 !grecaptcha.getResponse() 检查的原因。 除非挑战得到解决,否则它不会返回任何内容。 差不多就是这样 - 我希望能有所帮助:)
作为一个肮脏的解决方法,我们可以设置超时并等待recaptcha iframe显示然后等待它隐藏
我制作了进行所有操作的模块
它取决于 jquery 和全局 recaptcha
我像这样使用它
try {
key = await captcha(elementToBind, 'yoursitekey');
}
catch (error) {
console.log(error); // when recaptcha canceled it will print captcha canceled
}
不好的部分,当谷歌更改 html 结构中的某些内容时,它可能会中断
模块代码
/* global grecaptcha */
import $ from "jquery";
let callback = () => {};
let hideCallback = () => {};
export default function captcha (el, sitekey) {
const $el = $(el);
el = $el[0];
let captchaId = $el.attr("captcha-id");
let wrapper;
if (captchaId == null) {
captchaId = grecaptcha.render(el, {
sitekey,
size: "invisible",
type: "image",
callback: result => callback(result),
badge: "inline",
});
$(el).attr("captcha-id", captchaId);
}
else {
grecaptcha.reset(captchaId);
}
const waitForWrapper = setInterval(() => {
// first we search for recaptcha iframe
const iframe = $("iframe").filter((idx, iframe) => iframe.src.includes("recaptcha/api2/bframe"));
iframe.toArray().some(iframe => {
const w = $(iframe).closest("body > *");
// find the corresponding iframe for current captcha
if (w[0] && !w[0].hasAttribute("captcha-id") || w.attr("captcha-id") == captchaId) {
w.attr("captcha-id", captchaId);
wrapper = w; // save iframe wrapper element
clearInterval(waitForWrapper);
return true;
}
});
}, 100);
const result = new Promise((resolve, reject) => {
callback = (result) => {
clearInterval(waitForHide);
resolve(result);
};
hideCallback = (result) => {
clearInterval(waitForHide);
reject(result);
};
});
grecaptcha.execute(captchaId);
let shown = false;
const waitForHide = setInterval(() => {
if (wrapper) { // if we find iframe wrapper
if (!shown) {
// waiting for captcha to show
if (wrapper.css("visibility") !== "hidden") {
shown = true;
console.log("shown");
}
}
else {
// now waiting for it to hide
if (wrapper.css("visibility") === "hidden") {
console.log("hidden");
hideCallback(new Error("captcha canceled"));
}
}
}
}, 100);
return result;
}
我创建了一个 dom 观察者来检测验证码何时附加到 DOM,然后我断开它(因为不再需要它)并将点击处理程序添加到它的背景元素。
请记住,此解决方案对 DOM 结构的任何更改都很敏感,因此如果 google 出于任何原因决定更改它,它可能会中断。
还记得清理观察者/听众,在我的情况下(反应)我在 useEffect 的清理功能中进行。
const captchaBackgroundClickHandler = () => {
...do whatever you need on captcha cancel
};
const domObserver = new MutationObserver(() => {
const iframe = document.querySelector("iframe[src^=\"https://www.google.com/recaptcha\"][src*=\"bframe\"]");
if (iframe) {
domObserver.disconnect();
captchaBackground = iframe.parentNode?.parentNode?.firstChild;
captchaBackground?.addEventListener("click", captchaBackgroundClickHandler);
}
});
domObserver.observe(document.documentElement || document.body, { childList: true, subtree: true });
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.