如何在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
相关产品推荐
相关产品推荐

