如何在不刷新页面的情况下实时更新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>
说明
- 把服务器端的
clock_in时间转为毫秒级时间戳传给前端,避免时区问题 - 在前端JS中,每次计算当前时间与
clock_in的差值,转换为HH:MM:SS格式 - 使用
setInterval替代setTimeout,更适合周期性重复执行的场景 - 移除了服务器端计算
elapsed_time的代码,改为前端实时计算
内容的提问来源于stack exchange,提问作者Daniel_Rocha
相关产品推荐
相关产品推荐

