我正在用这样的代码渲染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模式的背景,recaptcha关闭,我无法检测到它,所以我等待无限响应
我需要某种事件或回调来检测它什么时候关闭
答案 0 :(得分:1)
作为一种肮脏的解决方法,我们可以设置超时并等待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;
}
答案 1 :(得分:0)
很遗憾,Google没有API事件来跟踪此事件,但是我们可以使用Mutation Observer Web API来通过Google API自行跟踪DOM更改。
我们在这里面临2个挑战。
1)检测何时显示挑战并获取挑战的叠加层
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 challenge"]');
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
};
然后,我们创建了一个函数,当观察者检测到我们在config对象中指定的特定类型的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 challenge"]');
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']
});
});
所以我们要做的是获得Google reCaptcha挑战叠加层以及在步骤1中创建的Promise,然后我们对叠加div上的“样式”更改进行了订阅。这是因为当挑战结束时-Google淡出了挑战。 重要的是要注意,当一个人成功解决了验证码时,可见性也会被隐藏。这就是为什么我们添加了!grecaptcha.getResponse()检查的原因。除非解决了挑战,否则它将不会返回任何内容。 差不多就可以了-我希望能有所帮助:)