You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在不刷新页面的情况下实时更新elapsed_time计时

问题描述

我需要让elapsed_time每秒更新一次,但目前只有刷新页面时它才会更新。我在脚本中使用了setTimeout,希望通过下方代码实现计数器每秒更新:

shifts_controller.rb

class User::ShiftsController < ApplicationController
  before_action :authenticate_user!
  before_action :set_shift, only: %i[show edit update destroy]

  # GET /shifts or /shifts.json
  def index
    @shifts = current_user.shifts.order(id: :desc)
    @shift  = current_user.shifts.where(clock_out: nil).where.not(clock_in: nil).first
  end

  # GET /shifts/1 or /shifts/1.json
  def show; end

  # GET /shifts/new
  def new
    @shift = Shift.new
  end

  # POST /shifts
  def create
    @shift = Shift.new(user: current_user) # quando criar ja liga ao id
    respond_to do |format|
      if @shift.save
        format.html do
          redirect_to action: 'index', user_id: current_user,
                      notice: 'Shift was successfully created.'
        end
      else
        format.html { render :new, status: :unprocessable_entity }
      end
    end
  end

  # GET /shifts/1/edit
  def edit; end

  # PATCH/PUT /shifts/1
  def update
    @shift.set_clock_out
    respond_to do |format|
      if @shift.save
        format.html do
          redirect_to action: 'index', user_id: current_user,
                      notice: 'Shift was successfully updated.'
        end
      else
        format.html { render :edit, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /shifts/1 or /shifts/1.json
  def destroy
    @shift.destroy
    respond_to do |format|
      format.html do
        redirect_to action: 'index', user_id: current_user,
                    notice: 'Shift was successfully destroyed.'
      end
    end
  end

  private

  # Use callbacks to share common setup or constraints between actions.
  def set_shift
    @shift = Shift.find(params[:id])
  end

  # Only allow a list of trusted parameters through.
  def shift_params
    params.require(:shift).permit(:clock_in, :clock_out, :user_id, :user_name)
  end
end

index.html.erb

<div class='container'>
  <% if @shift&.clock_in %>
    <div class='card text-center'>
      <div id='time'>
        <%time_diff = @shift.clock_in - 0.1.second.ago%>
        <%elapsed_time = Time.at(time_diff.to_i.abs).strftime("%H:%M:%S")%>
      </div>
    </div>
    <div class='card'>
      <br /><%= link_to 'Clock Out', user_shift_path(@shift.id), method: :put, class: "btn btn-success mb-4"%>
    </div>
  <% else %>
    <div class='card'>
      <br /><%= link_to 'Clock In', user_user_shifts_path(current_user.id), method: :post, class: "btn btn-success mb-4"%>
    </div>
  <% end %>

  <%= render 'shifts', shifts: @shifts %>
</div>

<script>
(function timer () {
    document.getElementById("time").innerHTML = "<%=elapsed_time%>";
    setTimeout(timer, 1000); // recalls the function after 1000 ms
})();
</script>
解决方案

问题出在<%=elapsed_time%>是服务器在页面加载时计算的固定值,前端JS每次执行只会重复设置这个初始值,不会实时计算新的流逝时间。要实现每秒更新,需要把clock_in的时间传给前端,用JS在客户端实时计算:

修改index.html.erb中的对应部分:

<div class='container'>
  <% if @shift&.clock_in %>
    <div class='card text-center'>
      <div id='time'></div>
    </div>
    <div class='card'>
      <br /><%= link_to 'Clock Out', user_shift_path(@shift.id), method: :put, class: "btn btn-success mb-4"%>
    </div>
  <% else %>
    <div class='card'>
      <br /><%= link_to 'Clock In', user_user_shifts_path(current_user.id), method: :post, class: "btn btn-success mb-4"%>
    </div>
  <% end %>

  <%= render 'shifts', shifts: @shifts %>
</div>

<script>
// 获取服务器传来的clock_in时间(转为毫秒级时间戳)
const clockInTimestamp = <%= @shift.clock_in.to_i * 1000 %>;

function updateElapsedTime() {
    const now = Date.now();
    const elapsedMs = now - clockInTimestamp;
    // 计算小时、分钟、秒
    const hours = Math.floor(elapsedMs / (1000 * 60 * 60));
    const minutes = Math.floor((elapsedMs % (1000 * 60 * 60)) / (1000 * 60));
    const seconds = Math.floor((elapsedMs % (1000 * 60)) / 1000);
    // 格式化为HH:MM:SS,不足两位补0
    const formattedTime = `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`;
    document.getElementById("time").textContent = formattedTime;
}

// 初始化显示一次
updateElapsedTime();
// 每秒更新一次
setInterval(updateElapsedTime, 1000);
</script>

说明

  1. 把服务器端的clock_in时间转为毫秒级时间戳传给前端,避免时区问题
  2. 在前端JS中,每次计算当前时间与clock_in的差值,转换为HH:MM:SS格式
  3. 使用setInterval替代setTimeout,更适合周期性重复执行的场景
  4. 移除了服务器端计算elapsed_time的代码,改为前端实时计算

内容的提问来源于stack exchange,提问作者Daniel_Rocha

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 08:54:21