Предполагаемый эффект приведенного ниже кода заключается в том, что он отображает 2 красных круга, которые становятся серыми, если на них навести указатель мыши. Код Javascript находится внутри SVG по определенной причине, и мне нравится сохранять его таким. Эта конкретная причина заключается в том, что мне приходится мириться с тем, как работают наборы символов Inkscape.
.hovered {
fill: gray !important; /* Apply the gray fill color */
stroke: blue !important; /* Add a blue border */
stroke-width: 8px !important; /* Set border width */
}<!-- JavaScript class for managing general behavior -->
<!-- SVG containing the symbol -->
<svg style = "display: none;">
<symbol id = "redCircle" viewBox = "0 0 200 200">
<circle cx = "50" cy = "50" r = "40" fill = "red" />
<script><![CDATA[
// Wait for the document to be fully loaded before setting up event listeners
document.addEventListener('DOMContentLoaded', () => {
// Add mouseover and mouseout event handlers to each <use> element
const useElements = document.querySelectorAll('[*|href = "#redCircle"]');
useElements.forEach(useElement => {
const color = useElement.getAttribute('data-color') || 'red';
useElement.addEventListener('mouseover', () => {
console.info("hover");
useElement.classList.add('hovered');
});
useElement.addEventListener('mouseout', () => {
useElement.classList.remove('hovered');
});
});
});
]]></script>
</symbol>
</svg>
<!-- SVG containing two <use> references to the symbol with color properties -->
<svg id = "svg-container">
<use xlink:href = "#redCircle" id = "use1" x = "100" data-color = "blue" />
<use xlink:href = "#redCircle" id = "use2" data-color = "green" />
</svg>Если я удалю fill = "red" из символа, он сделает то, что я хочу.
но я хочу, чтобы код работал независимо от атрибута "fill = " символа.
Итак, вот кое-что, что я попробовал:
This CSS works, but only if I don't apply fill = "red" to the symbol:
<style>
.hovered {
fill: gray; /* Apply the gray fill color */
stroke: blue; /* Add a blue border */
stroke-width: 8px; /* Set border width */
}
</style>
This CSS doesn't work at all:
<style>
.hovered circle {
fill: gray; /* Apply the gray fill color */
stroke: blue; /* Add a blue border */
stroke-width: 8px; /* Set border width */
}
</style>
This CSS doesn't work at all:
<style>
/* CSS to define the hovered class */
.hovered use circle {
fill: gray; /* Apply the gray fill color */
stroke: blue; /* Add a blue border */
stroke-width: 8px; /* Set border width */
}
</style>
and this CSS is the only one that can override the fill = "red":
<style>
circle {
fill: gray; /* Apply the gray fill color */
stroke: blue; /* Add a blue border */
stroke-width: 8px; /* Set border width */
}
</style>
Некоторые из вас просили меня подробно объяснить, почему я «слишком усложняю».
Надеюсь, это поможет...
Это связано с xlink:href вместо href.
Я не эксперт, но это хорошая статья
В самом деле! Я читал это несколько раз!
Я не думаю, что вы сможете сделать это, не установив цвет по умолчанию (красный) в качестве свойства CSS. Если все в порядке, попробуйте #svg-container { fill: red; } и circle { fill: inherit; }
сброс цвета заливки, как предложил Джеймс, должен помочь. будьте осторожны: при использовании xlink:href вам также следует добавить атрибут пространства имен xmlns:xlink = "http://www.w3.org/1999/xlink" или переключиться на href (или для лучшей обратной совместимости добавьте оба). См. пример кода . Вы также можете использовать переменные CSS, если ваша основная цель — установить отдельные стили наведения. пример2
@Джеймс, к сожалению, это не то, что я ищу, нет...
@herrstrietzel спасибо за совет! Я обязательно добавлю это...
@herrstrietzel Гораздо лучше использовать fill:red вместо родительского элемента, как я пытался.
@HereticMonkey [*|attr] — это элемент, имеющий атрибут attr, определенный в любом пространстве имен.
Уточните пожалуйста: 1. зачем вообще нужен какой-то JS 2. почему нельзя просто так скинуть атрибут fill в символ. 3. почему вы используете атрибут xlink:href в пространстве имен 4. что должен делать этот атрибут данных? Судя по вашему текущему описанию, большинство членов SO решат, что вы слишком усложняете ситуацию. Возможно, ваша конечная цель — развернуть этот SVG как внешний источник с прикрепленными к нему более сложными событиями (в этом случае вы столкнетесь с другими проблемами). Поэтому, пожалуйста, пересмотрите свой вопрос.
Я изменил свой код, чтобы все теги <use> были заменены тегами <svg>, тем самым теряя теневой DOM и связанные с ним ограничения. Простите, но вы этого не сделали... уточните, пожалуйста.
@herrstrietzel Я изменил свой код, поэтому все теги <use> заменены тегами <svg>, тем самым теряя теневой DOM и связанные с ним ограничения. --> Я сделал это, но не в своем вопросе, потому что это изменило бы вопрос до такой степени, что это уже не вопрос, а обходной путь.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Я поставил id = "circleId" красному кругу.
Я отслеживаю и сохраняю его getElementId под const redC.
И измените цвет с помощью redC.style.fill= "gray" на mouseover и redC.style.fill= "red" на mouseout. Работает отлично.
.hovered {
fill: gray !important; /* Apply the gray fill color */
stroke: blue !important; /* Add a blue border */
stroke-width: 8px !important; /* Set border width */
} <svg style = "display: none;">
<symbol id = "redCircle" viewBox = "0 0 200 200">
<circle id = "circleId" cx = "50" cy = "50" r = "40" fill = "red" />
<script><![CDATA[
// Wait for the document to be fully loaded before setting up event listeners
document.addEventListener('DOMContentLoaded', () => {
// Add mouseover and mouseout event handlers to each <use> element
const redC= document.getElementById("circleId");
const useElements = document.querySelectorAll('[*|href = "#redCircle"]');
useElements.forEach(useElement => {
const color = useElement.getAttribute('data-color') || 'red';
useElement.addEventListener('mouseover', () => {
console.info("hover");
useElement.classList.add('hovered');
redC.style.fill= "gray";
});
useElement.addEventListener('mouseout', () => {
useElement.classList.remove('hovered');
redC.style.fill= "red";
});
});
});
]]></script>
</symbol>
</svg>
<!-- SVG containing two <use> references to the symbol with color properties -->
<svg id = "svg-container">
<use xlink:href = "#redCircle" id = "use1" x = "100" data-color = "blue" />
<use xlink:href = "#redCircle" id = "use2" data-color = "green" />
</svg>Я собираюсь попробовать ваш код как можно скорее. Это странно...
@Bigman74066 😐😐 Это вообще странно. Попробуйте удалить строки из класса .hovered и посмотрите, как он себя поведет.
Я попробовал ваш код, но он не работает в (Chrome). Какой браузер вы используете?
@ Bigman74066 Хром. Здесь все работает нормально. Только что проверил с помощью Firefox. Firefox окрашивает оба круга в серый цвет, в то время как в Chrome только тот, на который наведен курсор, меняет цвет. Можете ли вы проверить Firefox? jsfiddle.net/j8pz2xq1
@ Bigman74066 Проверено на мобильном телефоне (нажатие). На мобильном телефоне тоже работает нормально.
Я немного изменил ваш код, чтобы он соответствовал моему примеру, удалил JS и добавил :hover пользовательские свойства CSS.
Поскольку атрибуты данных HTML в настоящее время по-прежнему могут использоваться только как строковые результаты из CSS, я думаю, что вместо этого было бы лучше использовать пользовательские свойства CSS.
Базовый принцип
attr(..), установите цвет по умолчанию (устанавливает #svg-container { color: red })currentColor, чтобы установить необходимые цвета. Для использования на всей странице установите их на уровне #svg-container { --primary: blue; --secondary: --green /* extend at will */ }.:root:--dsp-color, простое прямое переопределение.
#use1 { --dsp-color: blue }, по умолчанию #use2 { --dsp-color: var(--primary, magenta) }, если magenta не определено.
--primary, по умолчанию #use3 { --dsp-color: var(--secondary, yellow) }, если yellow не определено.
расширяйте этот список по мере необходимости или вместо этого используйте классы.
Вместо этого вышеуказанные правила также могут быть определены в конкретном HTML --secondary как <use>, например. style = "..." и т. д.
<use id = "use1" style = "--dsp-color: blue">, чтобы наконец отобразить круг нужного цвета:--dsp-color, использует переменную цвета времени отображения и текущее родительское значение circle { fill: var(--dsp-color, currentColor) }. Это устанавливает соответствующий цвет, переопределяющий набор color в fill (который, по сути, стал излишним).Фрагмент прокомментирован и должен быть понятен в сочетании со всем вышеизложенным. Я добавил несколько дополнительных переменных, чтобы показать более общий эффект наведения.
/*
Main color definitions
*/
#svg-container {
/* change to any default color you require, sets 'currentColor' */
color: red;
/* disable these to see defaults in effect */
--primary : blue; /* disable to show magenta circle */
--secondary: green; /* disable to show yellow circle */
/**/
/* Optionally define hover effects */
--hover : gray;
--stroke : blue;
--str-width: 8px;
}
/*
Specific Color overrides per <use> case
When undefined, they fallback to a default color
*/
#use1 { --dsp-color: blue }
#use2 { --dsp-color: var(--primary , magenta) }
#use3 { --dsp-color: var(--secondary, yellow) }
/*
Color handling SVG circle
*/
#redCircle circle { /* optionally using #redCircle for more specificity */
/* When --dsp-color is undefined it defaults to 'currentColor' defined in #svg-container */
fill: var(--dsp-color, currentColor); /* overrides 'fill' in <circle> */
}
#redCircle circle:hover { /* optionally using #redCircle for more specificity */
fill : var(--hover, gray); /* Apply --hover fill color, default gray */
stroke: var(--stroke, blue); /* Apply --stroke color, default blue */
stroke-width: var(--str-width, 8px); /* Apply --str-width, default 8px */
cursor: pointer;
}<svg style = "display: none;">
<symbol id = "redCircle" viewBox = "0 0 400 200">
<circle cx = "50" cy = "50" r = "40" fill = "red" />
<!-- 'fill' is redundant here, gets overridden -->
</symbol>
</svg>
<!-- SVG containing four <use> references to the symbol with color properties -->
<svg id = "svg-container" style = "">
<use xlink:href = "#redCircle" id = "use1" x = "0" />
<use xlink:href = "#redCircle" id = "use2" x = "75" />
<use xlink:href = "#redCircle" id = "use3" x = "150" />
<use xlink:href = "#redCircle" id = "use4" x = "225" />
</svg>Я хочу, чтобы код работал независимо от атрибута «fill=" символа.
Вот простой подход CSS, в котором:
fill к элементам <use> (через id каждого элемента)<circle> наследует значение fill из шага выше через fill: inherit.Рабочий пример:
svg {
width: 100%;
height: 180px;
}
circle {
fill: inherit;
}
circle:hover {
fill: gray;
stroke: blue;
stroke-width: 8px;
}
use[href = "#redCircle"] {
fill: red;
}
#use1 {
fill: blue;
}
#use2 {
fill: green;
}<svg>
<defs>
<symbol id = "redCircle">
<circle cx = "50" cy = "50" r = "40" />
</symbol>
</defs>
<g id = "container">
<use id = "use1" href = "#redCircle" x = "0" />
<use id = "use2" href = "#redCircle" x = "100" />
<use id = "use3" href = "#redCircle" x = "200" />
<use id = "use4" href = "#redCircle" x = "300" />
</g>
</svg>Дальнейшее чтение:
Добавлено: Казалось, не будет места, где circle:hover не будет работать. Поэтому, когда я писал это для Firefox, я не проверял кроссбраузерность. Новость: совершенно непонятно почему, но circle:hover, похоже, не работает в движке рендеринга Blink (Chrome, Edge, Brave и т. д.)
Вы можете сбросить (старые) symbol и use, когда вам нужно создать несколько объектов SVG.
Определите стандартный веб-компонент, создавая каждый SVG и упрощая события.
Примечание: customElements.define также можно использовать перед анализом DOM
Все <svg-circle> обновятся автоматически, DOMContentLoaded вуду не требуется.
Если вы хотите сделать что-то более сложное, вы можете добавить shadowDOM, чтобы можно было ограничить значения идентификаторов CSS и SVG.
Когда вы создаете SVG с помощью innerHTML, вам не нужно задавать пространство имен. С помощью createElement вам необходимо установить правильное пространство имен SVG.
<style>
svg-circle { width: 120px; background: lightgreen; stroke:black }
</style>
<h3>Who is afraid of red, yellow and blue?</h3>
<svg-circle fill = "red" hover = "blue" ></svg-circle>
<svg-circle fill = "yellow" hover = "green"></svg-circle>
<svg-circle fill = "blue" hover = "red" ></svg-circle>
<svg-circle></svg-circle>
<script>
customElements.define('svg-circle', class extends HTMLElement {
connectedCallback() {
const fillcolor = this.getAttribute("fill") || "red";
this.style.display = "inline-block";
this.innerHTML = `<svg viewBox = "0 0 9 9">` +
`<circle cx = "50%" cy = "50%" r = "40%" fill = "${fillcolor}"/>` +
`</svg>`;
this.circle = Object.assign(this.querySelector("circle"), {
style : "cursor:pointer",
onmouseenter: e => this.fill = this.getAttribute("hover") || "blue",
onmouseleave: e => this.fill = fillcolor
})
}
set fill(color) {
this.circle.setAttribute("fill", color);
}
});
</script>Спасибо всем за ваши реакции! Однако некоторые из вас упустили из виду тот факт, что мне нравится хранить Javascript внутри SVG. Вот почему я сейчас перенес это требование в начало вопроса. Тем временем я изменил свой код, чтобы все теги <use> были заменены тегами <svg>, тем самым теряя теневой DOM и связанные с ним ограничения.
Затем отметьте этот комментарий Inkscape жирным шрифтом. И обратите внимание: Inkscape выдает раздутый SVG-код. Но да, похоже, тебе придется с этим жить
Как отмечалось ранее, предопределенные атрибуты заливки или обводки (примененные к дочерним элементам <symbol>) излишне усложняют стилизацию CSS <use> элементов.
При условии, что SVG вашего ресурса прикреплен к DOM, поэтому к нему можно получить доступ через правила CSS:
Следующий пример основан на неизмененном HTML/SVG с добавлением правил переопределения CSS.
svg{
border:1px solid #ccc;
overflow:visible;
}
/* inherit fill */
symbol *{
fill:inherit;
stroke:inherit;
}
/* set default fill */
use{
fill: red;
}
.hovered {
fill: gray;
stroke: blue;
stroke-width: 8px;
}<!-- SVG containing the symbol -->
<svg style = "display: none;">
<symbol id = "redCircle" viewBox = "0 0 200 200">
<circle cx = "50" cy = "50" r = "40" fill = "red" />
<script><![CDATA[
// Wait for the document to be fully loaded before setting up event listeners
document.addEventListener('DOMContentLoaded', () => {
// Add mouseover and mouseout event handlers to each <use> element
const useElements = document.querySelectorAll('[*|href = "#redCircle"]');
useElements.forEach(useElement => {
const color = useElement.getAttribute('data-color') || 'red';
useElement.addEventListener('mouseover', () => {
console.info("hover");
useElement.classList.add('hovered');
});
useElement.addEventListener('mouseout', () => {
useElement.classList.remove('hovered');
});
});
});
]]></script>
</symbol>
</svg>
<!-- SVG containing two <use> references to the symbol with color properties -->
<svg id = "svg-container">
<use xlink:href = "#redCircle" id = "use1" x = "100" data-color = "blue" />
<use xlink:href = "#redCircle" id = "use2" data-color = "green" />
</svg>Очевидно, что при таком подходе мы не можем применять динамическую заливку по умолчанию при изменении ресурсов SVG, а только фиксированное значение, такое как «красный», жестко запрограммированное в CSS.
При условии, что вы можете исправить встроенный JS в свои ресурсы SVG, вы также можете отключить предопределенные атрибуты заливки, чтобы облегчить стилизацию CSS:
svg{
border:1px solid #ccc;
overflow:visible;
}
.hovered {
fill: gray;
stroke: blue;
stroke-width: 8px;
}<!-- SVG containing the symbol -->
<svg style = "display: none;">
<symbol id = "redCircle" viewBox = "0 0 200 200">
<circle cx = "50" cy = "50" r = "40" fill = "red" />
<script><![CDATA[
// Wait for the document to be fully loaded before setting up event listeners
document.addEventListener('DOMContentLoaded', () => {
// remove fill from use reference/symbol
const symbolEl = document.querySelector('#redCircle circle');
const symbolElFill = symbolEl.getAttribute('fill');
symbolEl.removeAttribute('fill');
// Add mouseover and mouseout event handlers to each <use> element
const useElements = document.querySelectorAll('[*|href = "#redCircle"]');
useElements.forEach(useElement => {
// apply default fill to use elements/symbol instances
useElement.setAttribute('fill', symbolElFill);
//const color = useElement.getAttribute('data-color') || 'red';
useElement.addEventListener('mouseover', () => {
useElement.classList.add('hovered');
});
useElement.addEventListener('mouseout', () => {
useElement.classList.remove('hovered');
});
});
});
]]></script>
</symbol>
</svg>
<!-- SVG containing two <use> references to the symbol with color properties -->
<svg id = "svg-container" xmlns:xlink = "http://www.w3.org/1999/xlink">
<use xlink:href = "#redCircle" id = "use1" x = "100" data-color = "blue" />
<use xlink:href = "#redCircle" id = "use2" data-color = "green" />
</svg>По сути, мы удаляем заливки по умолчанию во встроенном JS следующим образом:
// remove fill from use reference/symbol
const symbolEl = document.querySelector('#redCircle circle');
const symbolElFill = symbolEl.getAttribute('fill');
symbolEl.removeAttribute('fill');
<use>.В этом случае мы запрашиваем все элементы <use>, находим определяющие элементы, на которые имеются ссылки, и удаляем их стили по умолчанию.
statesForUseInstances();
function statesForUseInstances() {
// Add mouseover and mouseout event handlers to each <use> element
const useElements = document.querySelectorAll("use");
// get element in symbol
let ref = useElements[0].getAttribute("xlink:href");
let symboEl = document.querySelector(`${ref}`).children[0];
// remove fill from use reference/symbol
symboEl.removeAttribute('fill')
useElements.forEach((useElement) => {
useElement.addEventListener("mouseover", () => {
useElement.classList.add("hovered");
});
useElement.addEventListener("mouseout", () => {
useElement.classList.remove("hovered");
});
});
}svg{
border:1px solid #ccc;
overflow:visible;
width:10em;
}
.blue{
fill: blue;
}
.green{
fill: green;
}
.hovered {
fill: gray;
stroke: blue;
stroke-width: 8px;
}<!-- SVG containing the symbol -->
<svg style = "position:absolute;width:0;height:0;overflow:hidden;visibility:hidden;">
<symbol id = "redCircle" viewBox = "0 0 100 100">
<circle cx = "50" cy = "50" r = "40" fill = "red" />
</symbol>
</svg>
<!-- SVG containing two <use> references to the symbol with color properties -->
<svg viewBox = "0 0 200 100" xmlns:xlink = "http://www.w3.org/1999/xlink">
<use xlink:href = "#redCircle" x = "0" width = "100" class = "blue" />
<use xlink:href = "#redCircle" x = "100" width = "100" class = "green" />
</svg>
<svg viewBox = "0 0 100 100" xmlns:xlink = "http://www.w3.org/1999/xlink">
<use xlink:href = "#redCircle" id = "use1" class = "blue" />
</svg>
<svg viewBox = "0 0 100 100" xmlns:xlink = "http://www.w3.org/1999/xlink">
<use xlink:href = "#redCircle" id = "use2" class = "green" />
</svg><symbol>xlink:href классифицируется как устаревший. Основная причина, по которой вам следует развернуть его, — это обратная совместимость с графическими редакторами или офисными приложениями (которые обычно не поддерживают более современные стандарты SVG) — в этом случае вам также следует добавить атрибут namespace к родительскому элементу SVG xmlns:xlink = "http://www.w3.org/1999/xlink". Если ваш SVG доступен только в браузерах, просто замените его на href.<use> элементам для выбора CSS, чтобы сохранить низкую специфичность ваших правил. В противном случае вы получите слишком много переопределяющих правил, требующих important! или вложенных селекторов.Идея 2 великолепна! Сам никогда об этом не думал!
'[*|href = "#redCircle"]'выбирает все (*) или элементы с определенным атрибутомhref. Похоже, вторая половина этого селектора избыточна...