Can I change SVG "xlink:href" of image tag just use CSS? - css

My SVG pattern Shown below
<pattern id="img1" patternUnits="userSpaceOnUse" width="10" height="10">
<image xlink:href="Buzz.jpg" x="0" y="0" width="10" height="10" />
</pattern>
<pattern id="img2" patternUnits="userSpaceOnUse" width="10" height="10">
<image xlink:href="Tank-icon2.png" x="0" y="0" width="10" height="10" />
</pattern>
Now, I want to change xlink:href="Buzz.jpg" to xlink:href="Buzz2.jpg". Can I just do it by CSS?

No, you cannot use CSS to change HTML tag attributes.

Related

Change position of pattern in one path but does not affect other pathes with the same pattern in svg

I have filled lots of paths with a same pattern. These paths are in the same class.
I want to change the position of the pattern in one of the paths individually, to put the pattern at the place I want, but do not affect the pattern position in the other paths. How should I do this?
For example, here I have filled three rectangles with a pattern. I want to change the position of the pattern in rectA by dragging the slider. Currently in my code, it will change the position of the pattern in all three shapes at the same time.
<script src="https://unpkg.com/d3#5.15.1/dist/d3.min.js"></script>
<script src="https://unpkg.com/d3#5.15.1/dist/d3.min.js"></script>
<script src="https://d3js.org/d3.v4.js"></script>
<script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script>
<svg width="300" height="100">
<defs>
<pattern id="myPattern"
x="0" y="0" width="40" height="40"
patternUnits="userSpaceOnUse" patternTransform="rotate(0)">
<rect id="rotateRect" x="5" y = "5" width = "30" height = "30" fill = " #87CEFA "/>
</pattern>
</defs>
<rect id="rectA" class="myClass" x="0" y="0" width="100" height="100"
style="stroke: #000000;" />
<rect id="rectB" class="myClass" x="100" y="0" width="100" height="100"
style="stroke: #000000;" />
<rect id="rectC" class="myClass" x="200" y="0" width="100" height="100"
style="stroke: #000000;" />
</svg>
<p style=font-size:15px>Move Pattern in Rect A </p>
<input id="slider" type="range" min="0" max="100" value="5" step='1' >
<script>
d3.selectAll(".myClass")
.attr("fill", "url(#myPattern)")
const slider = document.getElementById("slider")
const myPattern = document.getElementById("myPattern")
slider.oninput = function(){
myPattern.setAttribute("patternTransform", "translate(0"+","+slider.value+") ")
}
</script>
The result I want is like this:
Root cause is the pattern id is a global id; the first defined id will set the pattern on all shapes.
So use a unique pattern id for each pattern
or place the <svg> in a native Web Component with shadowDOM,
so all id values are local (to shadowDOM) values:
<svg-squares></svg-squares>
<svg-squares transform="25"></svg-squares>
<svg-squares transform="66"></svg-squares>
<script>
customElements.define("svg-squares", class extends HTMLElement {
connectedCallback() {
this.style.display = "inline-block";
this.attachShadow({mode:"open"})
.innerHTML = `
<svg width="100" height="100">
<defs>
<pattern id="P" width="40" height="40" patternUnits="userSpaceOnUse">
<rect x="5" y="5" width ="30" height="30" fill="#87CEFA"/>
</pattern>
</defs>
<rect fill="url(#P)" width="100" height="100" stroke="black" x="0" y="0"/>
</svg>` +
// slider
`<br><input type="range" min="0" max="100" value="5"
oninput="this.getRootNode().host.transform(this.value)">`;
this.transform(this.getAttribute("transform")||0);
}
transform(value) {
this.shadowRoot
.querySelector("pattern")
.setAttribute("patternTransform", `translate(0 ${value})`)
}
})
</script>
You can also easily create "child" patterns using the href attribute.
So these new patterns are based and more importantly linked to the initial pattern template.
<svg width="300" height="100">
<defs>
<pattern id="basePattern" x="0" y="0" width="40" height="40" patternUnits="userSpaceOnUse" patternTransform="rotate(0)">
<rect id="rotateRect" x="5" y="5" width="30" height="30" fill="#87CEFA" />
</pattern>
<!-- child pattern will inherit graphics from base pattern -->
<pattern id="childPattern" href="#basePattern" class="childPattern" style="color:blue" patternTransform="rotate(45) scale(0.25)" />
</defs>
<rect id="rectA" class="rect" x="0" y="0" width="100" height="100" style="stroke: #000000;" fill="url(#basePattern)"></rect>
<rect id="rectB" class="rect" x="100" y="0" width="100" height="100" style="stroke: #000000;" fill="url(#childPattern)"></rect>
</svg>
Actually similar to <use> references. However in many aspects quite different, since a referenced pattern can't inherit properties like fill colors.
<svg width="300" height="100">
<defs>
<pattern id="basePattern" x="0" y="0" width="40" height="40" patternUnits="userSpaceOnUse" patternTransform="rotate(0)">
<rect id="patternRect" x="5" y="5" width="30" height="30" fill=" #87CEFA " />
</pattern>
</defs>
<rect id="rectA" class="rect" x="0" y="0" width="100" height="100" style="stroke: #000000;" />
<rect id="rectB" class="rect" x="100" y="0" width="100" height="100" style="stroke: #000000;" />
<rect id="rectC" class="rect" x="200" y="0" width="100" height="100" style="stroke: #000000;" />
</svg>
<p>
A <input class="sliderPattern" id="sliderA" type="range" min="0" max="3" value="1" step='0.1'> B <input class="sliderPattern" id="sliderB" type="range" min="0" max="3" value="1" step='0.1'> C <input class="sliderPattern" id="sliderC" type="range" min="0"
max="3" value="1" step='0.1'>
</p>
<p><button id="randomColor">Random Color (applied to base pattern)</button>
</p>
<script>
const basePattern = document.getElementById("basePattern");
const patternDefs = basePattern.closest('svg').querySelector('defs');
const rects = document.querySelectorAll('.rect');
const slider = document.getElementById("slider")
//duplicate patterns
for (let i = 0; i < rects.length; i++) {
let rect = rects[i];
let newPattern = document.createElementNS('http://www.w3.org/2000/svg', 'pattern');
newPattern.id = "childPattern" + i;
newPattern.setAttribute("href", "#basePattern");
newPattern.classList.add("childPattern");
rect.setAttribute('fill', `url(#childPattern${i})`);
patternDefs.appendChild(newPattern);
}
const patterns = document.querySelectorAll('.childPattern');
const sliders = document.querySelectorAll('.sliderPattern');
sliders.forEach(function(slider, i) {
slider.addEventListener("change", (e) => {
let scale = e.currentTarget.value;
let patternTransformBase = basePattern.getAttribute("patternTransform");
patterns[i].setAttribute("patternTransform", patternTransformBase + ` scale(${scale})`)
})
});
randomColor.addEventListener('click', (e) => {
let random = Math.random();
let newCol = `hsl(${180*random}deg 30% 60%)`;
patternRect.style.fill = newCol
});
</script>

SVG elements CSS transition inside <g> tag on Chrome

So I've created a page with some colorful SVG images in it, and I want them to be grayed at hormal state, and shows color while hovered.
svg {
width: 200px;
margin: 50px;
}
svg * {
transition: fill 1s;
}
svg:not(:hover) * {
fill: gray !important;
}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 3 1">
<rect x="0" y="0" width="1" height="1" style="fill: red" />
<rect x="1" y="0" width="1" height="1" style="fill: green" />
<rect x="2" y="0" width="1" height="1" style="fill: blue" />
</svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 3 1">
<g>
<rect x="0" y="0" width="1" height="1" style="fill: red" />
<rect x="1" y="0" width="1" height="1" style="fill: green" />
<rect x="2" y="0" width="1" height="1" style="fill: blue" />
</g>
</svg>
As one can see, SVGs have different colored elements, also some elements are grouped. This is pretty simplified example, but the real images are much more complex, with massive transform-s so I can't easily remove grouping.
Both images work perfect and changes colors while hovered, but the first image does it instantly while the the second has 1 second delay before animation starts.
Searching for the solution I've found that if an elemend is wrapped with a single <g> tag it has the animation delay, but if there no <g> or two of them, no delay occurs.
Firefox animates both images with no animation delay.
By the moment I ungrouped elements by hands, but obviously it's not a good solution, so the question is how it can be solved without changing files at all ?
A pretty sneaky bug, but easily solved: just restrict the child selector to non-g elements:
svg {
width: 200px;
margin: 50px;
}
svg :not(g) {
transition: fill 1s;
}
svg:not(:hover) * {
fill: gray !important;
}
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 3 1">
<rect x="0" y="0" width="1" height="1" style="fill: red" />
<rect x="1" y="0" width="1" height="1" style="fill: green" />
<rect x="2" y="0" width="1" height="1" style="fill: blue" />
</svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 3 1">
<g>
<rect x="0" y="0" width="1" height="1" style="fill: red" />
<rect x="1" y="0" width="1" height="1" style="fill: green" />
<rect x="2" y="0" width="1" height="1" style="fill: blue" />
</g>
</svg>

SVG bars graph upside down without rotation

I have an svg rect chart like:
<div style="width:300px;height:300px;">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" style="width:100%;height:100%" viewBox="0 0 300 300">
<g>
<rect width="14.55" height="40%" x="0" y="0" fill="black"></rect>
<rect width="14.55" height="20%" x="50" y="0" fill="green"></rect>
<rect width="14.55" height="80%" x="100" y="0" fill="red"></rect>
<rect width="14.55" height="90%" x="150" y="0" fill="yellow"></rect>
<rect width="14.55" height="10%" x="200" y="0" fill="pink"></rect>
<rect width="14.55" height="60%" x="250" y="0" fill="orange"></rect>
</g>
</svg>
</div>
What I want to do is to display it upside down.
The code and a given solution, are coming from http://jsfiddle.net/rhvP8/5/
Although, I want to keep each bar in the same X place. So, rotation approach is not that useful in this case.
Any help is welcome.
Rotation of the x-axis only seems be be what you require.
svg {
border: 1px solid green;
transform: rotateX(180deg);
}
<div style="width:300px;height:300px;">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" style="width:100%;height:100%" viewBox="0 0 300 300">
<g>
<rect width="14.55" height="40%" x="0" y="0" fill="black"></rect>
<rect width="14.55" height="20%" x="50" y="0" fill="green"></rect>
<rect width="14.55" height="80%" x="100" y="0" fill="red"></rect>
<rect width="14.55" height="90%" x="150" y="0" fill="yellow"></rect>
<rect width="14.55" height="10%" x="200" y="0" fill="pink"></rect>
<rect width="14.55" height="60%" x="250" y="0" fill="orange"></rect>
</g>
</svg>
</div>

SVG clip path hole / inner cut

<clipPath id="clip1">
<rect x="10" y="222" height="30" width="50" rx="5" />
</clipPath>
<image .... clip-path="url(#clip1)" />
It cuts the outer space of the image. But I want to cut a hole into the image. How can I achieve it?
For your purposes, you can use a mask
<svg width="200" height="200" viewBox="0 0 200 200">
<defs>
<mask id="hole">
<rect width="100%" height="100%" fill="white"/>
<rect x="50" y="50" height="50" width="100" rx="5" fill="black" />
</mask>
</defs>
<image x="0" y="0" width="200" height="200"
xlink:href="http://placeimg.com/200/200/any"
mask="url(#hole)" >
</image>
</svg>

How to create 2 separate SVG without blocking other div elements?

I have a page that's divided into 3 sections. A top bar with several buttons, a large middle section and a bottom bar with more buttons. And on top of everything several floating windows that will open and close depending on the buttons clicked (ignorable for this problem).
The requested structure is the middle section must be an empty div that will load several other pages through jquery. Some of those pages have interactive elements, so it must accept mouse events, despite the div having the lowest z-index of all the page elements.
The buttons on the top and bottom bars must all be svg elements, both because of the vectorial properties and the occasional odd shapes. And they must have a higher z-index than the middle div at all times.
My problem is the svg is covering the middle div and blocking all interactions (in this case a simple alert when you click on the div), although none of the visual elements overlap.
I've tried to call 2 different svgs, one for each bar, but the 2nd never shows up. I've also tried calling the 2 svgs inside a main svg and include the div inside as well, but it didn't work too.
This is an example of how it currently looks (the absolute positions, z-indexes and styles are defined in a css):
<div id="middleDiv" class="middleDiv" onClick="alert(1)">< /div>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<svg id="topBar" class="topBar" x="0" y="0">
<rect id="btn_1" class="topBtn" x="0" y="0" height="30" width="100"/>
<rect id="btn_2" class="topBtn" x="100" y="0" height="30" width="100"/>
</svg>
<svg id="bottomBar" class="bottomBar" x="0" y="500">
<rect id="btn_3" class="topBtn" x="0" y="0" height="30" width="100"/>
<rect id="btn_4" class="topBtn" x="100" y="0" height="30" width="100"/>
</svg>
</svg>
This was the 1st solution attempt:
<div id="middleDiv" class="middleDiv" onClick="alert(1)">< /div>
<svg id="topBar" class="topBar" x="0" y="0" xmlns="http://www.w3.org/2000/svg" version="1.1">
<rect id="btn_1" class="topBtn" x="0" y="0" height="30" width="100"/>
<rect id="btn_2" class="topBtn" x="100" y="0" height="30" width="100"/>
</svg>
<svg id="bottomBar" class="bottomBar" x="0" y="500" xmlns="http://www.w3.org/2000/svg" version="1.1">
<rect id="btn_1" class="topBtn" x="0" y="0" height="30" width="100"/>
<rect id="btn_2" class="topBtn" x="100" y="0" height="30" width="100"/>
</svg>
And this was the 2nd solution attempt:
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<svg id="topBar" class="topBar" x="0" y="0">
<rect id="btn_1" class="topBtn" x="0" y="0" height="30" width="100"/>
<rect id="btn_2" class="topBtn" x="100" y="0" height="30" width="100"/>
</svg>
<div id="middleDiv" class="middleDiv" onClick="alert(1)">< /div>
<svg id="bottomBar" class="bottomBar" x="0" y="500">
<rect id="btn_3" class="topBtn" x="0" y="0" height="30" width="100"/>
<rect id="btn_4" class="topBtn" x="100" y="0" height="30" width="100"/>
</svg>
</svg>
Firstly you should specify width and height attributes for the <svg> elements. If you're using Chrome it incorrectly makes them the size of the page if you omit such attributes and this may be your entire issue.
If that doesn't work then add pointer-events="none" to prevent the <svg> elements intercepting mouse clicks.

Resources