PAYATHON 2026

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,但它们不能代替服务端权限校验。

备注:内容仅供参考。