【问题标题】:A repeating countdown timer that continues even after browser refresh一个重复的倒数计时器,即使在浏览器刷新后也会继续
【发布时间】:2018-07-12 16:01:41
【问题描述】:

首先,我对 javascript 非常陌生。我有一个 Shopify 商店,我计划像亚马逊一样为我的每个产品页面设置一个倒数计时器。我知道有很多插件可以用于 Shopify,但它们都不符合我 Shopify 商店的主题和风格,所以我打算自己制作一个。

我想要的是,如果用户 1 打开我的网站并导航到产品页面,他应该会看到一个倒计时到特定时间的计时器,例如 12:00:00 (hh:mm:ss)。假设 User-1 看到 'Deal ends in 11:20:10' 现在如果 User-2 同时打开同一个产品页面,那么他也应该看到 'Deal ends in 11:20:10' 重点是计时器不应该每次都刷新回12:00:00浏览器加载/重新加载页面,网站上的每个用户都应该看到倒数计时器上的剩余时间。

我开始进行一些研究,并设法在我的商店中运行了一个计时器。这不是我想要的,但这里是代码:

    var interval;
    var minutes = 1;
    var seconds = 5;
    window.onload = function() {
        countdown('countdown');
    }

    function countdown(element) {
        interval = setInterval(function() {
            var el = document.getElementById(element);
            if(seconds == 0) {
                if(minutes == 0) {
                    el.innerHTML = "countdown's over!";                    
                    clearInterval(interval);
                    return;
                } else {
                    minutes--;
                    seconds = 60;
                }
            }
            if(minutes > 0) {
                var minute_text = minutes + (minutes > 1 ? ' minutes' : ' minute');
            } else {
                var minute_text = '';
            }
            var second_text = seconds > 1 ? 'seconds' : 'second';
            el.innerHTML = minute_text + ' ' + seconds + ' ' + second_text + ' remaining';
            seconds--;
        }, 1000);
    }
    </script>

这就是它的样子:

它确实有效,但存在以下问题:

  1. 它会随着浏览器的刷新而刷新。
  2. 没有时间。
  3. 计时器到零时不会自动重复。
  4. 每个用户的剩余时间都不一样。

正如我所提到的,我几乎是 JavaScript 的菜鸟。谁能帮我建立一个可以克服上述问题的倒数计时器?

【问题讨论】:

  • 由于您希望多个用户看到相同的计时器,因此仅 js 将无济于事,因为它是客户端
  • 您需要对您的服务器进行一些调用,该调用需要以原子方式处理。单靠 Js 帮不了你
  • 您首先需要计算当前时间(即用户登陆您的页面的时间)和目标时间(计时器应该为零的时间)之间的差异。然后用时差填写你函数中使用的小时、分钟、秒。我不确定为什么人们建议这应该在服务器端完成。这看起来很愚蠢。
  • 不要考虑倒计时应该运行多长时间。想想它应该什么时候结束,只存储那个信息(然后让你的 GUI 倒数到那个时间戳)。

标签: javascript


【解决方案1】:

说明

问题1:随着浏览器的刷新而刷新。

原因:因为您硬编码了秒和分钟(var seconds = 5var minutes = 1),所以每个访问该页面的用户都会看到计时器从完全相同的值“1 分钟”开始倒计时还剩 5 秒”一次又一次。

解决方案:不要硬编码倒计时的开始值,而是硬编码截止日期。因此,不要对页面的每个访问者说“从 1 分 5 秒开始倒计时到 0!”,而必须说“从现在到午夜的任何时间倒计时!”。这样,倒计时将始终在您网站的不同用户之间同步(假设他们的计算机时钟设置正确)。

问题 2:没有小时数。

原因:您的代码只有变量来跟踪秒和分钟,没有编写代码来跟踪小时。

解决方案:就像问题1的解决方案中提出的那样:不要跟踪剩余的小时/分钟/秒,而只跟踪截止日期然后计算小时/分钟/秒剩余时间基于当前客户端时间。

问题3:定时器到零时不会自动重复。

原因:您的代码中嵌套的ifs(检查seconds == 0m == 0)明确声明显示文本“倒计时结束!”倒计时结束时。

解决方案:保留一个检查倒计时何时结束的条件,而不是显示“倒计时结束!”,将截止日期重置为下一个截止日期。

问题 4:每个用户的剩余时间都不一样。

原因:见问题1

解决方案:参见问题 1

示例代码

这里有一段代码,整合了上面提到的解决方案:

const span = document.getElementById('countdown')

const deadline = new Date
deadline.setHours(0)
deadline.setMinutes(0)
deadline.setSeconds(0)

function displayRemainingTime() {
  if (deadline < new Date) deadline.setDate(deadline.getDate() + 1)
  const remainingTime = deadline - new Date
  const extract = (maximum, factor) => Math.floor((remainingTime % maximum) / factor)
  const seconds = extract(   60000, 1000   ) 
  const minutes = extract( 3600000, 60000  ) 
  const hours   = extract(86400000, 3600000)
  const string = `${hours} hours ${minutes} minutes ${seconds} seconds remaining`
  span.innerText = `${hours} hours ${minutes} minutes ${seconds} seconds remaining`
}
window.setInterval(displayRemainingTime, 1000)
displayRemainingTime()
<h3>
  <span id="countdown"></span>
</h3>

编辑:确保客户端时间正确

如果您不相信您的客户会正确安排他们的时间。您还可以从为您的页面提供服务的服务器发送正确的时间戳。由于我不知道您使用的是哪种服务器,因此我无法为您的服务器代码提供示例。但基本上您需要用您在服务器上生成的正确时间戳替换const trustedTimestamp = (即(new Date).getTime())之后的代码(来自以下示例)。有关此时间戳的正确格式,请参阅Date.prototype.getTime()

const span = document.getElementById('countdown')
const trustedTimestamp = (new Date).getTime()
let timeDrift = trustedTimestamp - (new Date)
const now = () => new Date(timeDrift + (new Date).getTime())

const deadline = now()
deadline.setHours(0)
deadline.setMinutes(0)
deadline.setSeconds(0)

function displayRemainingTime() {
  if (deadline < now()) deadline.setDate(deadline.getDate() + 1)
  const remainingTime = deadline - now()
  const extract = (maximum, factor) => Math.floor((remainingTime % maximum) / factor)
  const seconds = extract(   60000, 1000   ) 
  const minutes = extract( 3600000, 60000  ) 
  const hours   = extract(86400000, 3600000)
  span.innerText = `${hours} hours ${minutes} minutes ${seconds} seconds remaining`
}
window.setInterval(displayRemainingTime, 1000)
displayRemainingTime()
<h3>
  <span id="countdown"></span>
</h3>

来自谷歌服务器的时间

为了创建一个工作示例,我添加了这个实验,我可以从 Google 页面获取正确的时间。请勿在您的网站上使用此代码,因为不能保证 google 会一直托管此网页。

const span = document.getElementById('countdown')
const trustedTimestamp = (new Date).getTime()
let timeDrift = 0
const now = () => new Date(timeDrift + (new Date).getTime())

const deadline = now()
deadline.setHours(0)
deadline.setMinutes(0)
deadline.setSeconds(0)

window.setInterval(displayRemainingTime, 1000)
window.setInterval(syncClock, 3000)
displayRemainingTime()
syncClock()

function displayRemainingTime() {
  if (deadline < now()) deadline.setDate(deadline.getDate() + 1)
  const remainingTime = deadline - now()
  const extract = (maximum, factor) => Math.floor((remainingTime % maximum) / factor)
  const seconds = extract(   60000, 1000   ) 
  const minutes = extract( 3600000, 60000  ) 
  const hours   = extract(86400000, 3600000)
  span.innerText = `${hours} hours ${minutes} minutes ${seconds} seconds remaining`
}

function syncClock() {
  const xmlhttp = new XMLHttpRequest();
  xmlhttp.open("HEAD", "http://www.googleapis.com",true);
  xmlhttp.onreadystatechange = function() {
    const referenceTime = new Date
    if (xmlhttp.readyState==4) {
      const correctTime = new Date(xmlhttp.getResponseHeader("Date"))
      timeDrift = correctTime - referenceTime
    }
  }
  xmlhttp.send(null);
}
<h3>
  <span id="countdown"></span>
</h3>

【讨论】:

  • 达米安你是男人 :D ... 非常感谢 .. 你帮我省了很多痛苦 :D .. 很好的解释,真的很感谢所有的帮助 :) 答案很完美,但是我在想一件事。正如您所说,它依赖于用户机器的时间,所以我在想我们是否可以以某种方式从互联网上检查参考时间,然后相应地显示剩余时间?这样,每个用户都会看到正确的剩余时间,无论他们的机器上有什么时间。用js可以吗?
  • 我用一个关于如何使用来自互联网的参考时间的解决方案编辑了我的答案。如果这能解决您的问题,请考虑accepting my answer
  • 嗨 Damiaan :) 我已接受您的回答,并且在网站上运行良好。想知道我是否使用您的代码的发送版本(从谷歌服务器获取时间的代码?)..那么服务器停止发送时间信息的可能性有多大?
  • 这个实验很可能会停止工作。网络(技术)不断变化。没有网站会在 1 年内保持不变,它们会得到更新。
  • 好吧,写deadline.setHours(6) 您将截止日期设置为早上 6 点。但这不是你说你打算做的。当您达到最后期限时,您需要将其再移动 6 小时。所以换句话说,你将不得不 setHoursMath.floor(now().getHours() + 6 - now().getHours()%6) 之类的东西
【解决方案2】:

我不确定 Shopify 是如何处理插件的,但必须有一些方法可以为它们编辑 CSS,这样你就可以让它们与你的主题风格相匹配。

您在此处所做的,在您提供的 JS 中,将始终具有这种行为,因为它是客户端代码。该脚本将在每次刷新后运行,并将始终将值 minutesseconds 刷新为关联的初始值,即 15。我相信这回答了您的第一个问题。

根据您的第二个问题,您显然没有在该 JS 脚本中编码 hours。所以hours不应该出现,如果出现了那就是个谜了!

关于您的第三个问题,您在onload 函数期间只调用一次countdown

根据第一个答案,时钟不应该在用户之间同步是很自然的,因为从逻辑上讲,他们会在不同的时间刷新您的页面。

所以我认为你最好的选择是使用其中一个插件并只修改 CSS。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-20
    • 2014-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多