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

如何在Rails 7应用中点击SVG时用JavaScript切换文本标签

实现点击切换访问状态及文本更新

1. 调整视图模板(_park_cards.erb)

把文本和SVG放到同一个可点击容器中,让文本也能触发点击事件,同时给状态文本加标识方便JS操作:

<% @parks.each do |park| %>
...
  <div class="mt-1 btn p-0" 
       data-controller="visits"
       data-user-logged-in="<%= user_signed_in? %>"
       data-user-id="<%= current_user&.id %>"
       data-park-id="<%= park.id %>"
       data-visit-id="<%= park.visits.find_by(user: current_user)&.id %>"
       data-visited="<%= park.visited_by?(current_user) %>"
       data-action="click->visits#visit">
    <%# 给状态文本加class,方便控制器定位 %>
    <span class="visit-status-text"><%= park.visited_by?(current_user) ? '已访问' : '未访问' %></span>
    <svg
         stroke="<%= current_user && current_user.visited_parks.include?(park) ? '#5FA777' : '#D3D3D3' %>"
         stroke-width="<%= current_user && current_user.visited_parks.include?(park) ? 4 : 2 %>"
         fill="none"
         width="24"
         height="24"
         xmlns="http://www.w3.org/2000/svg"
         viewBox="0 0 24 24"
         class="w-6 h-6 ml-2">
         <path
           stroke-linecap="round"
           stroke-linejoin="round"
           d="M4.5 12.75l6 6 9-13.5" />
     </svg>
   </div>
...
<% end %>

修改说明:

  • 把data-controller、data-action和所有data属性移到外层div,点击文本或SVG都会触发visits#visit方法
  • 初始文本根据park.visited_by?(current_user)动态渲染,确保页面加载时状态正确
  • 给状态文本添加visit-status-text类,方便控制器定位元素

2. 更新Stimulus控制器(visits_controller.js)

添加更新状态文本的逻辑,在请求成功后切换文本内容:

import { Controller } from '@hotwired/stimulus';
import axios from 'axios';

export default class extends Controller {
  HEADERS = { 'ACCEPT': 'application/json' };

  visit() {
    if (this.element.dataset.userLoggedIn === 'false' ) {
      return document.querySelector(".sign-in-link").click();
    }

    if (this.element.dataset.visited === 'true') {
      this.unvisitPark();
    } else {
      this.visitPark();
    }
  }

  getVisitPath() {
    return '/api/visits';
  }

  getUnvisitPath(visitId) {
    return `/api/visits/${visitId}`
  }

  // 获取状态文本元素
  getStatusTextElement() {
    return this.element.querySelector('.visit-status-text');
  }

  // 更新状态文本
  updateStatusText(isVisited) {
    const textElement = this.getStatusTextElement();
    if (textElement) {
      textElement.textContent = isVisited ? '已访问' : '未访问';
    }
  }

  unvisitPark() {
    axios.delete(this.getUnvisitPath(this.element.dataset.visitId), {
      headers: this.HEADERS
    })
    .then((response) => {
      this.element.dataset.visited = 'false'
      this.element.dataset.visitId = '';
      // 获取SVG子元素并修改样式
      const svg = this.element.querySelector('svg');
      svg.setAttribute('stroke', '#D3D3D3');
      svg.setAttribute('stroke-width', '2');
      // 更新状态文本
      this.updateStatusText(false);
    });
  }

  visitPark() {
    axios.post(this.getVisitPath(), {
      user_id: this.element.dataset.userId,
      park_id: this.element.dataset.parkId
    }, {
      headers: this.HEADERS
    })
    .then((response) => {
      this.element.dataset.visited = 'true'
      this.element.dataset.visitId = response.data.id;
      // 获取SVG子元素并修改样式
      const svg = this.element.querySelector('svg');
      svg.setAttribute('stroke', '#5FA777');
      svg.setAttribute('stroke-width', '4');
      // 更新状态文本
      this.updateStatusText(true);
    });
  }
}

修改说明:

  • 新增getStatusTextElement和updateStatusText方法,封装文本操作逻辑,代码更易维护
  • 在unvisitPark和visitPark的请求成功回调中,调用updateStatusText切换文本内容
  • 调整SVG的获取方式:控制器现在绑定在外层div,通过this.element.querySelector('svg')定位SVG元素

3. 功能验证

  • 页面加载时,状态文本和SVG样式会根据用户是否访问过公园自动匹配
  • 点击文本或SVG,未登录状态会触发登录跳转;登录状态下会发送请求,同步更新SVG样式和文本内容
  • 请求完成后,data-visited属性同步更新,确保后续点击逻辑正确

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.02 21:50:38