Simple jQuery paywall 中 SetTimeout 与 var reg 失效问题
结论
问题出在变量作用域上。register() 中的 var reg = true 声明了一个新的局部变量,并没有修改 $(document).ready() 中点击事件读取的 reg。
同时,setTimeout() 启动后不会因为用户完成注册而自动取消。即使注册状态已经正确更新,注册前创建的定时器仍可能在 2 秒后显示 paywall。
解决方法是把注册状态和定时器 ID 放在同一个作用域中,并在注册成功后调用 clearTimeout()。
推荐修改
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script>
$(document).ready(function () {
let registered = false;
let paywallTimer = null;
$(".video").show();
$(".paywall").hide();
$(".video").on("click", function () {
if (registered) {
return;
}
// 避免连续点击创建多个定时器
if (paywallTimer !== null) {
clearTimeout(paywallTimer);
}
paywallTimer = setTimeout(function () {
// 定时器执行前再次确认注册状态
if (!registered) {
$(".video").hide();
$(".paywall").show();
}
paywallTimer = null;
}, 2000);
});
$(".form").on("click", function () {
registered = true;
// 取消注册前已经启动的 paywall 定时器
if (paywallTimer !== null) {
clearTimeout(paywallTimer);
paywallTimer = null;
}
$(".video").show();
$(".paywall").hide();
alert("You have been registered");
});
});
</script>
</head>
<body>
<div class="video">Video Player</div>
<div class="paywall">PAYWALL</div>
<hr>
<div class="form">Form</div>
</body>
</html>
原代码为什么失效
原代码中其实有两个不同的 reg:
$(document).ready(function () {
var reg = false;
});
这里的 reg 只在 ready 回调及其内部的事件处理函数中可见。
下面的代码又声明了一个局部变量:
function register() {
var reg = true;
}
这个 reg 只在 register() 执行期间存在。函数执行结束后,它无法再被访问,也不会影响点击事件所读取的 reg。所以视频点击事件看到的值一直是 false。
两种写法的区别是:
var reg = true; // 声明一个新的局部变量
reg = true; // 修改当前可访问作用域中的已有变量
不过,在当前代码中直接删除 var 也解决不了问题,因为 register() 位于 ready 回调之外,无法访问回调中的 reg。更清晰的做法是把注册逻辑放进同一个闭包,同时避免污染全局作用域。
为什么还需要取消定时器
假设用户先点击视频,下面的定时器已经开始倒计时:
setTimeout(function () {
$(".video").hide();
$(".paywall").show();
}, 2000);
如果用户在 2 秒内完成注册,只执行以下代码还不够:
registered = true;
$(".video").show();
$(".paywall").hide();
之前安排的回调仍会继续执行,并再次隐藏视频。因此,需要保存定时器 ID:
paywallTimer = setTimeout(...);
注册成功后再取消定时器:
clearTimeout(paywallTimer);
定时器回调执行前再次检查 registered,可以多加一层保护,避免注册状态变化后仍然显示 paywall。
可以删除的 credit 变量
示例中的 credit 没有被其他代码读取:
var credit = true;
var credit = false;
这两个变量分别位于不同函数中,都是局部变量,既不会保留状态,也不会共享状态。当前需求只需要 registered,可以删除 credit,以免干扰状态判断。
实际使用时的注意事项
前端变量只能控制界面,不能作为真正的付费或授权凭证。用户可以修改 JavaScript、清除页面状态,甚至直接请求视频资源。正式的 paywall 应由服务器验证登录、订阅或支付状态,并在服务端对视频地址、分片请求或播放令牌进行授权。
如果刷新页面后仍需保留注册状态,可以从服务端会话中读取。纯演示环境也可以使用 sessionStorage 或 localStorage,但它们不能代替服务端权限校验。
备注:内容仅供参考。
