이 오래된 게시물을 다시 살려주세요: Changing a single instance of an icon
사이트 전체에서 아이콘의 단일 인스턴스만 변경해야 합니다. replaceIcon()을 사용해 해당 아이콘의 모든 인스턴스를 오버라이드하고 싶지 않습니다. 과거에는 사이트의 특정 영역에 대해 이것이 가능하도록 만들었다고 보이지만, 사이트 전체에는 적용되지 않는 것으로 보입니다.
예를 들어, 검색창의 톱니바퀴 아이콘 중 하나만 사용자가 클릭했을 때 발생하는 동작을 더 잘 반영하도록 변경하고 싶습니다. 모든 톱니바퀴 아이콘을 변경하고 싶은 것은 아닙니다.
감사합니다!
1개의 좋아요
이 PR를 확인해 보세요. CSS만으로도 비슷한 효과를 낼 수 있을 겁니다:
main ← ux-ranked-choice-bullets
merged 03:10PM - 28 Oct 24 UTC
Extending @merefield's PR a bit (https://github.com/discourse/discourse/pull/285… 93)
This gives the ranked choice polls a distinctive preview style, rather than the default radio buttons. This more closely mimics the button styles in the live post:
The live poll:

Preview before:

Preview after:


3개의 좋아요
이건 좀 해킹 같은 방법인데, 탭 키로 이동하거나 키보드만으로 조작할 때 어떻게 작동하나요?
2개의 좋아요
사막에 물이 전혀 없는 것처럼 보일 때, 해킹(지름길)은 정말 유용할 수 있습니다!
2개의 좋아요
아쉽게도 우리는 사막에 있는 게 아닙니다… 우리는 공개 사이트를 호스팅하기 위해 플랫폼 사용료를 내고 있는 것이죠.
2개의 좋아요
이 아이콘은 구체적으로 무엇이며(어디에 표시됩니까)? 다른 사용자들에게도 유용할 만한 합리적인 이유가 있다고 판단되면, 별도로 교체할 수 있도록 별칭을 생성할 수 있습니다.
2개의 좋아요
확장 검색 페이지를 여는 버튼의 동작을 반영하지 않는 코그(cog)의 예시를 하나 보여 드리겠습니다:
1개의 좋아요
기본적으로 여기에는 “슬라이더” 아이콘이 사용되며(기본 설정에서 이 아이콘이 Discourse에 등장하는 유일한 경우입니다) — 따라서 이 경우 replaceIcon()을 사용하는 것이 안전합니다.
개별 아이콘을 교체할 수 있는 API가 없기 때문에, 현재 이 문제를 지원할 수 있는 최선의 방법은 유사한 사용 사례를 그룹화하는 새로운 별칭 추가 요청을 평가하는 것입니다.
예를 들어, heart의 별칭으로 d-liked를 사용하여 앱 전체의 하트 아이콘 모든 발생을 교체하는 대신 replaceIcon()을 d-liked에 적용하여 좋아요 컨텍스트에서만 아이콘을 변경할 수 있습니다.
import { h } from "virtual-dom";
import { isDevelopment } from "discourse-common/config/environment";
import attributeHook from "discourse-common/lib/attribute-hook";
import deprecated from "discourse-common/lib/deprecated";
import escape from "discourse-common/lib/escape";
import I18n from "discourse-i18n";
export const SVG_NAMESPACE = "http://www.w3.org/2000/svg";
let _renderers = [];
let warnMissingIcons = true;
let _iconList;
export const REPLACEMENTS = {
"d-tracking": "bell",
"d-muted": "discourse-bell-slash",
"d-regular": "far-bell",
"d-watching": "discourse-bell-exclamation",
"d-watching-first": "discourse-bell-one",
"d-drop-expanded": "caret-down",
This file has been truncated. show original
물론 단일 발생만 교체할 수 있다면 좋겠지만, 테마에서는 이 상황이 흔합니다 — 언젠가 이를 위한 API가 제공되기를 바랍니다.
대단하네요! 디스코ourse 팀이 우리의 설정을 설정 코그로 변경한 것 같아서 제가 수정해 두었습니다! 감사합니다
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();
}
// Font Awesome 아이콘(fa-feather-pointed)을 위한 새로운 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";
}
});
</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>
Don
5월 15, 2025, 8:41오후
12
수동으로 아이콘을 추가하는 대신 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개의 좋아요
Don
5월 16, 2025, 6:18오전
14
네, 맞아요! 이건 좀 불안정하네요. requestAnimationFrame이나 MutationObserver를 사용하면 더 안정적일 수도 있겠네요. 하지만 말씀하신 대로 전용 API가 가장 좋겠죠.
2개의 좋아요
좋은 지적 감사합니다 — 말씀이 정확합니다. api.onPageChange만 사용하면 신뢰성이 떨어질 수 있습니다. 이는 DOM 렌더링 이후가 아니라 라우트 변경 시점에 발화되기 때문입니다. 즉, 스크립트가 #create-topic 버튼에 접근하려 할 때 해당 버튼이 아직 존재하지 않을 수 있는 레이스 컨디션(race condition) 위험이 있습니다.
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를 포함했습니다. 불필요한 오버헤드를 피하기 위해 작업을 완료한 후에는 스스로 연결을 해제합니다. 요소가 이미 존재할 경우를 대비해 즉시 검사도 시도합니다.
Font Awesome에는 있는 것으로 보이지만 Discourse에는 없는 것 같아서, 관리자 SVG 아이콘 하위 집합으로 등록해 두었음에도 불구하고 Discourse 아이콘 시스템에서 원하는 SVG를 가져오지 못하고 있습니다.
1개의 좋아요