diff --git a/resources/js/chat-room.js b/resources/js/chat-room.js
index b503a71..7bcbf07 100644
--- a/resources/js/chat-room.js
+++ b/resources/js/chat-room.js
@@ -62,6 +62,7 @@ export {
} from "./chat-room/preferences-status.js";
export { bindChatRightPanelControls } from "./chat-room/right-panel.js";
export {
+ bindRoomStatusControls,
normalizeRoomStatus,
renderRoomStatusRow,
renderRoomsOnlineStatus,
@@ -131,6 +132,7 @@ import {
} from "./chat-room/preferences-status.js";
import { bindChatRightPanelControls } from "./chat-room/right-panel.js";
import {
+ bindRoomStatusControls,
normalizeRoomStatus,
renderRoomStatusRow,
renderRoomsOnlineStatus,
@@ -207,6 +209,7 @@ if (typeof window !== "undefined") {
setSoundMuted,
shouldMigrateLocalChatPreferences,
bindChatRightPanelControls,
+ bindRoomStatusControls,
normalizeRoomStatus,
renderRoomStatusRow,
renderRoomsOnlineStatus,
@@ -261,6 +264,7 @@ if (typeof window !== "undefined") {
bindShopControls();
bindVipControls();
bindChatRightPanelControls();
+ bindRoomStatusControls();
bindMobileDrawerControls();
bindWelcomeMenuControls();
bindBlockMenuControls();
diff --git a/resources/js/chat-room/rooms.js b/resources/js/chat-room/rooms.js
index 584bc87..400b070 100644
--- a/resources/js/chat-room/rooms.js
+++ b/resources/js/chat-room/rooms.js
@@ -6,6 +6,9 @@ import { escapeHtml } from "./html.js";
const EMPTY_ROOMS_HTML =
'
暂无房间
';
+// 事件委托只需要注册一次,避免房间在线状态定时刷新后重复绑定。
+let roomStatusControlsBound = false;
+
/**
* 转换接口房间数据,过滤异常房间编号。
*
@@ -39,18 +42,52 @@ export function resolveRoomUrl(roomId, roomUrlResolver = undefined) {
}
/**
- * 生成可放入 onclick 属性的安全跳转语句。
- * 这里暂时保留属性字符串,是为了兼容现有 HTML 字符串渲染入口。
- * URL 先 JSON 字符串化再转义,避免地址内容突破属性上下文。
+ * 生成房间跳转数据属性,实际跳转由事件委托统一处理。
+ * 只输出 data 属性,避免在线房间列表继续混入内联 onclick。
*
* @param {number} roomId
* @param {(roomId:number) => string} [roomUrlResolver]
* @returns {string}
*/
-function buildRoomClickHandler(roomId, roomUrlResolver = undefined) {
- const safeUrlLiteral = escapeHtml(JSON.stringify(resolveRoomUrl(roomId, roomUrlResolver)));
+function buildRoomClickAttributes(roomId, roomUrlResolver = undefined) {
+ const safeUrl = escapeHtml(resolveRoomUrl(roomId, roomUrlResolver));
- return `onclick="location.href=${safeUrlLiteral}"`;
+ return `data-room-url="${safeUrl}"`;
+}
+
+/**
+ * 绑定房间列表跳转事件。
+ * 右侧面板和手机抽屉都复用 data-room-url,刷新 HTML 后无需重新绑定。
+ *
+ * @returns {void}
+ */
+export function bindRoomStatusControls() {
+ if (roomStatusControlsBound || typeof document === "undefined") {
+ return;
+ }
+
+ roomStatusControlsBound = true;
+
+ document.addEventListener("click", (event) => {
+ if (!(event.target instanceof Element)) {
+ return;
+ }
+
+ const roomLink = event.target.closest("[data-room-url]");
+
+ if (!roomLink) {
+ return;
+ }
+
+ const roomUrl = roomLink.getAttribute("data-room-url");
+
+ if (!roomUrl) {
+ return;
+ }
+
+ event.preventDefault();
+ window.location.href = roomUrl;
+ });
}
/**
@@ -73,14 +110,14 @@ export function renderRoomStatusRow(room, options = {}) {
: '当前')
: "";
// 当前房间不生成跳转事件,避免重复进入同一房间触发无意义刷新。
- const clickHandler = isCurrent ? "" : buildRoomClickHandler(room.id, options.roomUrlResolver);
+ const clickAttributes = isCurrent ? "" : buildRoomClickAttributes(room.id, options.roomUrlResolver);
const badge = room.online > 0
? `${room.online}${variant === "mobile" ? "" : " "}人`
: `空`;
// 手机和桌面只区分容器尺寸与视觉密度,房间数据口径保持同一套。
if (variant === "mobile") {
- return `
@@ -92,7 +129,7 @@ export function renderRoomStatusRow(room, options = {}) {
const border = isCurrent ? "#aac5f0" : "#e0eaf5";
- return `