我正在使用Google隐形recaptcha。有没有办法检测挑战窗口什么时候关闭?通过挑战窗口,我指的是您必须选择一些图像进行验证的窗口。
一旦按下按钮,我现在在按钮上放置一个微调器,该按钮呈现recaptcha挑战。无法通过另一个挑战窗口提示用户。
我以编程方式调用render函数:
grecaptcha.render(htmlElement, { callback: this.verified, expiredCallback: this.resetRecaptcha, sitekey: this.siteKey, theme: "light", size: "invisible" });
我有2个回调函数连接验证和resetRecaptcha函数,如下所示:
function resetRecaptcha() {
grecaptcha.reset();
}
function verified(recaptchaResponse)
{
/*
which calls the server to validate
*/
}
我原以为grecaptcha.render有另一个回调,当挑战屏幕关闭而没有用户通过选择图像验证自己时调用。
如您所述,API不支持此功能。
但是,您可以自己添加此功能。您可以谨慎使用以下代码,Google可能会更改其reCaptcha,并通过这样做来破坏此自定义代码。该解决方案依赖于reCaptcha的两个特性,因此如果代码不起作用,请先查看:
iframe
src:包含“google.com/recaptcha/api2/bframe”opacity
属性:窗口关闭时更改为0// to begin: we listen to the click on our submit button
// where the invisible reCaptcha has been attachtted to
// when clicked the first time, we setup the close listener
recaptchaButton.addEventListener('click', function(){
if(!window.recaptchaCloseListener) initListener()
})
function initListener() {
// set a global to tell that we are listening
window.recaptchaCloseListener = true
// find the open reCaptcha window
HTMLCollection.prototype.find = Array.prototype.find
var recaptchaWindow = document
.getElementsByTagName('iframe')
.find(x=>x.src.includes('google.com/recaptcha/api2/bframe'))
.parentNode.parentNode
// and now we are listening on CSS changes on it
// when the opacity has been changed to 0 we know that
// the window has been closed
new MutationObserver(x => recaptchaWindow.style.opacity == 0 && onClose())
.observe(recaptchaWindow, { attributes: true, attributeFilter: ['style'] })
}
// now do something with this information
function onClose() {
console.log('recaptcha window has been closed')
}
要在IE中工作,此解决方案需要.include()和Array.from()的polyfill,如下所示:
Array.from on the Internet Explorer
ie does not support 'includes' method
更新的代码:
function initListener(){
// set a global to tell that we are listening
window.recaptchaCloseListener = true
// find the open reCaptcha window
var frames = Array.from(document.getElementsByTagName('iframe'));
var recaptchaWindow;
frames.forEach(function(x){
if (x.src.includes('google.com/recaptcha/api2/bframe') ){
recaptchaWindow = x.parentNode.parentNode;
};
});
// and now we are listening on CSS changes on it
// when the opacity has been changed to 0 we know that
// the window has been closed
new MutationObserver(function(){
recaptchaWindow.style.opacity == 0 && onClose();
})
.observe(recaptchaWindow, { attributes: true, attributeFilter: ['style'] })
}
我的解决方案
let removeRecaptchaOverlayEventListener = null
const reassignGRecatchaExecute = () => {
if (!window.grecaptcha || !window.grecaptcha.execute) {
return
}
/* save original grecaptcha.execute */
const originalExecute = window.grecaptcha.execute
window.grecaptcha.execute = (...params) => {
try {
/* find challenge iframe */
const recaptchaIframe = [...document.body.getElementsByTagName('iframe')].find(el => el.src.match('https://www.google.com/recaptcha/api2/bframe'))
const recaptchaOverlay = recaptchaIframe.parentElement.parentElement.firstElementChild
/* detect when the recaptcha challenge window is closed and reset captcha */
!removeRecaptchaOverlayEventListener && recaptchaOverlay.addEventListener('click', window.grecaptcha.reset)
/* save remove event listener for click event */
removeRecaptchaOverlayEventListener = () => recaptchaOverlay.removeEventListener('click', window.grecaptcha.reset)
} catch (error) {
console.error(error)
} finally {
originalExecute(...params)
}
}
}
运行window.grecaptcha.render()
之后和window.grecaptcha.execute()
之前调用此函数
并且不要忘记删除事件监听器:removeRecaptchaOverlayEventListener()