Reputation: 123
Referring to the code-snipplet below about getting data with on click on an svg path here, for my example which is displaying Number 2, I would like it to have the alert button pop up when the cursor is clicked only when close to number 2. My code below is wrong because when cursor is clicked anywhere which is far way from the Number 2, the pop up alert box is still shown. I will really appreciate any help I can get :)
function getKey(button) {
let key = button.querySelectorAll('path')[0].dataset.key;
alert('key is ' + key)
}
<div onClick="getKey(this)">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="800pt" height="600pt" viewBox="0 0 800 600 " id="svg1">
<g enable-background="new">
<path data-key="12345" transform="matrix(1,0,0,-1,0,600)" stroke-width=".74" stroke-linecap="round" stroke-linejoin="round" fill="none" stroke="#000000" d="M 224.34 585.57 L 224.34 586.6 L 225.22 588.68 L 226.1 589.71 L 227.87 590.75 L 231.39 590.75 L 233.15 589.71 L 234.04 588.68 L 234.92 586.6 L 234.92 584.53 L 234.04 582.46 L 232.27 579.35 L 223.46 568.98 L 235.8 568.98 "/>
</g>
</svg>
</div>
Upvotes: 1
Views: 4968
Reputation: 2687
this
in an element onclick
attribute refers to the element
By setting an onclick
attribute to the div
containing the svg
, a click anywhere within the div
will call the function, and if this
is sent to the function as an argument, it will always refer to the div
element.
Once fired, your function is extracting the data attribute of the first path
tag inside the div
.
Instead, an eventListener
can be attached to the path
elements directly. When fired an event
is created, which has a target
property containing a reference to the tag that generated the event. It is that target
element from which you need to extract the data
attribute.
See: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener
This working snippet demonstrates attaching event listeners to your paths. (I have had to change the line width of your path as the click must hit the line for the event to be triggered, see end note)
let paths = document.querySelectorAll('path');
// paths is an html collection of all paths;
// attach event listeners to each path;
for (let i=0; i<paths.length; i++) {
paths[i].addEventListener('click', event => alert(event.target.dataset.key));
}
<div>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="800pt" height="600pt" viewBox="0 0 800 600 " id="svg1">
<g enable-background="new">
<path data-key="12345" transform="matrix(1,0,0,-1,0,600)" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" fill="none" stroke="#000000" d="M 224.34 585.57 L 224.34 586.6 L 225.22 588.68 L 226.1 589.71 L 227.87 590.75 L 231.39 590.75 L 233.15 589.71 L 234.04 588.68 L 234.92 586.6 L 234.92 584.53 L 234.04 582.46 L 232.27 579.35 L 223.46 568.98 L 235.8 568.98 "/>
</g>
</svg>
</div>
If you have many paths, the above process may not be memory-efficient because the function is duplicated for each of the paths. Instead, a single handler function can be used and called from each of the event listeners. Like this:
let paths = document.querySelectorAll('path');
// paths is an html collection of all paths;
// attach event listeners to each path;
for (let i=0; i<paths.length; i++) {
paths[i].addEventListener('click', displayAlert);
}
function displayAlert(event) {
alert(event.target.dataset.key)
}
<div>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="800pt" height="600pt" viewBox="0 0 800 600 " id="svg1">
<g enable-background="new">
<path data-key="12345" transform="matrix(1,0,0,-1,0,600)" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" fill="none" stroke="#000000" d="M 224.34 585.57 L 224.34 586.6 L 225.22 588.68 L 226.1 589.71 L 227.87 590.75 L 231.39 590.75 L 233.15 589.71 L 234.04 588.68 L 234.92 586.6 L 234.92 584.53 L 234.04 582.46 L 232.27 579.35 L 223.46 568.98 L 235.8 568.98 "/>
</g>
</svg>
</div>
displayAlert
function is referenced within the event listener without parentheses or argument. The event is passed automatically to the named external function (the declaration for which should include an argument if the automatically passed event
is to be used inside the function). In practice, you are unlikely to have memory problems with such a trivial function but I've included this for completeness.
Alternatively, you can attach a single event listener to the div
, and check that the target
of the event is a path
before displaying the data. This works because, although the event is attached to the div
, the target
of the event is set to whichever descendant of the div
was clicked:
let div = document.getElementById('svg-container');
div.addEventListener('click', event => {
if (event.target.tagName == 'path') alert(event.target.dataset.key)
});
<div id="svg-container">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="800pt" height="600pt" viewBox="0 0 800 600 " id="svg1">
<g enable-background="new">
<path data-key="12345" transform="matrix(1,0,0,-1,0,600)" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" fill="none" stroke="#000000" d="M 224.34 585.57 L 224.34 586.6 L 225.22 588.68 L 226.1 589.71 L 227.87 590.75 L 231.39 590.75 L 233.15 589.71 L 234.04 588.68 L 234.92 586.6 L 234.92 584.53 L 234.04 582.46 L 232.27 579.35 L 223.46 568.98 L 235.8 568.98 "/>
</g>
</svg>
</div>
note
If you have to use narrow strokes for your paths, it may be difficult for a user to place the cursor exactly on the line and so clicks may be missed. One way to mitigate this is to duplicate the paths, set the stroke of the the lower (first) one of each pair much wider and make it invisible by using the background colour. In the case of characters like your number 2, it might be simpler to draw an appropriately sized invisible rectangle behind each character and attach event listener to them instead, or as well as, the character paths (remembering to include the data attribute).
Upvotes: 4