更改单个图标实例

将此旧帖子带回:Changing a single instance of an icon

我需要更改网站上单个图标的实例。我不想使用 replaceIcon() 并覆盖该图标的所有实例。我看到过去他们为网站的特定区域提供了这种可能性,或者至少是这样写的,但不是在整个网站上。

例如,我想更改搜索中的一个齿轮图标,以更好地反映用户单击它时发生的情况。我不想更改所有齿轮图标。

谢谢!

1 个赞

看看这个 PR,你也许可以用纯 CSS 实现类似的效果:

3 个赞

这看起来很粗糙,当您通过 Tab 键/仅键盘浏览时,它效果如何?

2 个赞

当一个人在沙漠中且明显没有水时,技巧会非常有用!

2 个赞

唉,我们不在沙漠里……我们是在付费使用一个平台来托管一个公共网站。

2 个赞

这是哪个图标(它出现在哪里)?如果看起来有合理的理由让其他人觉得有用,我们可以为它创建一个别名,以便单独替换它。

2 个赞

这里有一个示例,其中齿轮图标未反映按钮打开扩展搜索页面的操作:

1 个赞

我们默认在此处使用“滑块”图标(这也是 Discourse 默认情况下唯一使用此图标的地方)——因此在这种情况下使用 replaceIcon() 是安全的

image

由于我们没有替换单个图标的 API,因此今天支持此功能的最佳方法是评估添加新别名的请求,这些别名将相似的用例分组。

例如,我们使用 d-liked 作为 heart 的别名,因此可以针对 d-liked 使用 replaceIcon() 来更改它在“喜欢”上下文中的显示,而不是替换应用程序中所有出现的“心形”图标。

不过,能够替换任何单个出现的情况会很好,这在主题中并不罕见——希望有一天我们能有相应的 API。

太棒了,看起来 discourse 团队已将我们的设置更改为设置齿轮,我已经修复了!谢谢 :pray:

2 个赞

好的,我现在似乎明白了,这个的结论是什么。很遗憾得知它要等到 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");
    }
  });
});
1 个赞

这可能会变得有点不稳定。

我相信 api.onPageChange 在路由变化时会触发。

那是在渲染之前。

因此存在元素在你试图查找和覆盖它们之前未被渲染的风险。

你可能会幸运,但这并不保证。因此,awesomerobot 提出如果有一个专门的 api 会更好……

@Don,我相信你的解决方案也有类似的问题)

2 个赞

是的,你说得对!这不太稳定。也许使用 requestAnimationFrameMutationObserver 会更稳定?:thinking: 但是的,最好的还是像你提到的那样,有一个专门的 API。

2 个赞

感谢您的指正——您说得完全正确。仅使用 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 中。

1 个赞