将此旧帖子带回:Changing a single instance of an icon
我需要更改网站上单个图标的实例。我不想使用 replaceIcon() 并覆盖该图标的所有实例。我看到过去他们为网站的特定区域提供了这种可能性,或者至少是这样写的,但不是在整个网站上。
例如,我想更改搜索中的一个齿轮图标,以更好地反映用户单击它时发生的情况。我不想更改所有齿轮图标。
谢谢!
将此旧帖子带回:Changing a single instance of an icon
我需要更改网站上单个图标的实例。我不想使用 replaceIcon() 并覆盖该图标的所有实例。我看到过去他们为网站的特定区域提供了这种可能性,或者至少是这样写的,但不是在整个网站上。
例如,我想更改搜索中的一个齿轮图标,以更好地反映用户单击它时发生的情况。我不想更改所有齿轮图标。
谢谢!
看看这个 PR,你也许可以用纯 CSS 实现类似的效果:
这看起来很粗糙,当您通过 Tab 键/仅键盘浏览时,它效果如何?
当一个人在沙漠中且明显没有水时,技巧会非常有用!
唉,我们不在沙漠里……我们是在付费使用一个平台来托管一个公共网站。
这是哪个图标(它出现在哪里)?如果看起来有合理的理由让其他人觉得有用,我们可以为它创建一个别名,以便单独替换它。
我们默认在此处使用“滑块”图标(这也是 Discourse 默认情况下唯一使用此图标的地方)——因此在这种情况下使用 replaceIcon() 是安全的

由于我们没有替换单个图标的 API,因此今天支持此功能的最佳方法是评估添加新别名的请求,这些别名将相似的用例分组。
例如,我们使用 d-liked 作为 heart 的别名,因此可以针对 d-liked 使用 replaceIcon() 来更改它在“喜欢”上下文中的显示,而不是替换应用程序中所有出现的“心形”图标。
不过,能够替换任何单个出现的情况会很好,这在主题中并不罕见——希望有一天我们能有相应的 API。
太棒了,看起来 discourse 团队已将我们的设置更改为设置齿轮,我已经修复了!谢谢 ![]()
好的,我现在似乎明白了,这个的结论是什么。很遗憾得知它要等到 API 更新后才能使用,所以我目前将继续使用我之前的方法。
@awesomerobot 这是我更新的代码,用于更改创建主题图标,它是一个不全局更改的 + 号
<script type="text/discourse-plugin" version="0.8">
api.onPageChange(() => {
const button = document.querySelector("#create-topic");
if (button) {
// 暂时隐藏按钮,以便替换图标
button.style.display = "none";
// 查找按钮内的 SVG
const oldIcon = button.querySelector("svg");
if (oldIcon) {
// 删除现有的图标 (SVG)
oldIcon.remove();
}
// 创建一个新的 SVG 以用于 Font Awesome 图标 (fa-feather-pointed)
const newIcon = document.createElementNS("http://www.w3.org/2000/svg", "svg");
newIcon.setAttribute("class", "fa d-icon d-icon-feather-pointed svg-icon svg-string");
newIcon.setAttribute("xmlns", "http://www.w3.org/2000/svg");
newIcon.innerHTML = '<use href="#feather-pointed"></use>';
// 将新图标插入按钮内
button.insertBefore(newIcon, button.firstChild);
// 图标替换后重新显示按钮
button.style.display = "block";
}
});
</script>
需要进一步优化以支持多个图标更改
<script type="text/discourse-plugin" version="0.8">
api.onPageChange(() => {
// 要替换的按钮选择器和对应的自定义 SVG 内容列表
const iconsToReplace = [
{ selector: "#create-topic", newSVG: `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<path d="M12 2L15 5H13V9H11V5H9L12 2ZM3 12L5 10V13H9V15H5V18L3 16V12ZM21 12L19 10V13H15V15H19V18L21 16V12Z" />
</svg>
` },
{ selector: "#some-other-button", newSVG: `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<path d="M21 3H3C2.44772 3 2 3.44772 2 4V20C2 20.5523 2.44772 21 3 21H21C21.5523 21 22 20.5523 22 20V4C22 3.44772 21.5523 3 21 3ZM20 19H4V5H20V19Z" />
</svg>
` },
{ selector: "#another-button", newSVG: `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
<path d="M12 2L8 6H10V10H14V6H16L12 2ZM12 12L8 16H10V20H14V16H16L12 12Z" />
</svg>
` }
];
iconsToReplace.forEach(icon => {
const button = document.querySelector(icon.selector);
if (button) {
// 暂时隐藏按钮,以便替换图标
button.style.display = "none";
// 查找按钮内的现有 SVG
const oldIcon = button.querySelector("svg");
if (oldIcon) {
// 删除现有的图标 (SVG)
oldIcon.remove();
}
// 创建一个新的 SVG 元素并设置其内容
const newIcon = document.createElementNS("http://www.w3.org/2000/svg", "svg");
newIcon.innerHTML = icon.newSVG; // 设置自定义 SVG 内容
// 将新的 SVG 图标插入按钮内
button.insertBefore(newIcon, button.firstChild);
// 图标替换后重新显示按钮
button.style.display = "block";
}
});
});
</script>
你也可以使用 iconHTML 来替代手动添加图标。
下面这样的代码应该可以工作:
import { apiInitializer } from "discourse/lib/api";
import { iconHTML } from "discourse/lib/icon-library";
export default apiInitializer((api) => {
api.onPageChange(() => {
const btnIcon = document.querySelector("#create-topic .d-icon");
if (btnIcon) {
btnIcon.outerHTML = iconHTML("plus");
}
});
});
这可能会变得有点不稳定。
我相信 api.onPageChange 在路由变化时会触发。
那是在渲染之前。
因此存在元素在你试图查找和覆盖它们之前未被渲染的风险。
你可能会幸运,但这并不保证。因此,awesomerobot 提出如果有一个专门的 api 会更好……
(@Don,我相信你的解决方案也有类似的问题)
是的,你说得对!这不太稳定。也许使用 requestAnimationFrame 或 MutationObserver 会更稳定?
但是的,最好的还是像你提到的那样,有一个专门的 API。
感谢您的指正——您说得完全正确。仅使用 api.onPageChange 可能会不可靠,因为它在路由更改时触发,而不是在 DOM 渲染后触发。这意味着存在竞态条件风险,即当脚本尝试访问 #create-topic 按钮时,该按钮可能尚不存在。
api.onPageChange(() => {
const targetSelector = "#create-topic";
const tryEnhanceButton = () => {
const button = document.querySelector(targetSelector);
if (button) {
// 找到元素后断开观察器连接
observer.disconnect();
// 暂时隐藏按钮,以便我们替换图标
button.style.display = "none";
// 删除旧图标(如果存在)
const oldIcon = button.querySelector("svg");
if (oldIcon) oldIcon.remove();
// 创建并插入新的 Font Awesome SVG 图标
const newIcon = document.createElementNS("http://www.w3.org/2000/svg", "svg");
newIcon.setAttribute("class", "fa d-icon d-icon-feather-pointed svg-icon svg-string");
newIcon.setAttribute("xmlns", "http://www.w3.org/2000/svg");
newIcon.innerHTML = '<use href="#feather-pointed"></use>';
button.insertBefore(newIcon, button.firstChild);
// 重新显示按钮
button.style.display = "block";
}
};
// 观察 DOM 更改,并在添加目标元素时尝试增强按钮
const observer = new MutationObserver(() => tryEnhanceButton());
observer.observe(document.body, { childList: true, subtree: true });
// 立即尝试,以防元素已存在
tryEnhanceButton();
});
@Don 我已包含 MutationObserver 以确保脚本仅在目标元素实际存在时运行,它会在完成工作后自行断开连接,以避免不必要的开销。它仍然会尝试立即检查,以防元素已存在。
我无法从 Discourse 图标系统中获取我想要的 SVG,尽管我已将其注册为管理员 SVG 图标子集,因为它似乎在 Font Awesome 中,但不在 Discourse 中。