Hi all,
I think I understand SVG clickmaps now, how to make them, how to troubleshoot them, and the like. But it took a long time to learn because I kept running into time consuming problems. There's documentation about those problems, but it's scattered all over the web, and much of it's contradictory. I'd still be trying to put it together if not for the help of several people on this mailing list, and I thank you for that.
So to ease the path of those who follow me, I put up a map of the landmines one's likely to run into while learning/using SVG clickmaps. Once a person knows these landmines exist, he/she can go about his/her business, and if something goes wrong, a simple reference to the landmines page substitutes for hours of web search and experimentation. Here's the URL of the SVG clickmaps landmines page:
http://troubleshooters.com/codecorn/svg_clickmaps/landmines.htm
SteveT
Steve Litt May 2017 featured book: Twenty Eight Tales of Troubleshooting http://www.troubleshooters.com/28
Steve,
On Mon, May 8, 2017 at 4:02 AM, Steve Litt <slitt@...2357...> wrote:
So to ease the path of those who follow me, I put up a map of the landmines one's likely to run into while learning/using SVG clickmaps. Once a person knows these landmines exist, he/she can go about his/her business, and if something goes wrong, a simple reference to the landmines page substitutes for hours of web search and experimentation. Here's the URL of the SVG clickmaps landmines page:
http://troubleshooters.com/codecorn/svg_clickmaps/landmines.htm
I have a few suggestions with regard to some of your "landmines" that might make things a little easier...
1) "Use alert() commands" Actually don't do this. Not for general logging of position or values in the script, at least. Alerts are intrusive and can only show you a text string. Get familiar with the developer console in all modern browsers, and especially the console.log() function. It's non-modal, and you can log out rich objects and data structures, then interrogate their properties, attributes and values interactively in the console. It's a much better experience for most cases when you might want to log via alert().
2) getElementsByTagName(), getElementsByClassName(), getElementById() These work, but are somewhat out of fashion now. document.querySelector() and document.querySelectorAll() are the preferred approach these days. They take a CSS selector as a parameter, which allows for much richer ways to select objects:
document.querySelectorAll(".myclass"); // Gets all the elements with a class of "myclass" document.querySelector(".myclass"); // The same, but only gets the first one
document.querySelector("#myID"); // Select by ID
document.querySelectorAll("circle"); // All the circles
document.querySelector("circle.myclass"); // The first circle with a class of "myclass"
document.querySelectorAll("g.myclass > circle"); // All the circles that are immediate children of a group with a class of "myclass"
CSS selectors are powerful things these days (though still with some limitations), and a well crafted querySelector() can save a lot of DOM traversal in code.
3) "Group Clicking Gotchas" "Let me start with this piece of advice: You'll probably *never* want to put events on the group itself."
I disagree with this advice. There's nothing wrong with putting event handlers on the group, or on the items within it, or on both. You just need to understand how events propagate within browsers; it's a slightly odd process, borne of a need to combine the old Internet Explorer way of handling them with the W3C standard way. In short, the things you need to know are these:
* "evt" will be the name of the event in the handler. * "evt.target" will be the object that fired the event (i.e. the object that was clicked on), not necessarily the one the handler is on. * "this" will be the name of the object the handler is attached to. * The handler can call another function, passing the event, but also passing other parameters. * If a child object wants to consume an event and not pass it up to its ancestors, you must call evt.stopPropagation()
I've put an example file up on my site which shows some of this at work:
http://peppertop.com/interactive_groups.svg
The whole file is a single group, but there are four onclick handlers (one on the group, one on each circle). Clicking a circle will change its colour randomly; clicking on the group (i.e. either lozenge shape) will change all three circles. There are also onmouseover and onmouseout handlers on the circles to thicken their borders when the mouse moves over them.
By putting a click handler on the group, and passing both "evt" and "this" you can get a reference to the object that was clicked ("evt.target") and the group itself ("this").
Regards,
Mark
P.S. Did you see the message I posted a couple of weeks ago about making AJAX calls from SVG? I saw no feedback on it, so I'm not sure if it made it to the mailing list or not.
On Mon, 8 May 2017 11:24:57 +0100 Mark Crutch <markc@...2744...> wrote:
Steve,
On Mon, May 8, 2017 at 4:02 AM, Steve Litt <slitt@...2357...> wrote:
So to ease the path of those who follow me, I put up a map of the landmines one's likely to run into while learning/using SVG clickmaps. Once a person knows these landmines exist, he/she can go about his/her business, and if something goes wrong, a simple reference to the landmines page substitutes for hours of web search and experimentation. Here's the URL of the SVG clickmaps landmines page:
http://troubleshooters.com/codecorn/svg_clickmaps/landmines.htm
I have a few suggestions with regard to some of your "landmines" that might make things a little easier...
- "Use alert() commands"
Actually don't do this. Not for general logging of position or values in the script, at least. Alerts are intrusive and can only show you a text string. Get familiar with the developer console in all modern browsers, and especially the console.log() function. It's non-modal, and you can log out rich objects and data structures, then interrogate their properties, attributes and values interactively in the console. It's a much better experience for most cases when you might want to log via alert().
Yes. I'll need to add an exhortation to use console.log(), as well as to reiterate that alert() is only for situations so unknown that only immediate feedback with stoppage of action will sufice.
- getElementsByTagName(), getElementsByClassName(), getElementById()
These work, but are somewhat out of fashion now. document.querySelector() and document.querySelectorAll() are the preferred approach these days. They take a CSS selector as a parameter, which allows for much richer ways to select objects:
document.querySelectorAll(".myclass"); // Gets all the elements with a class of "myclass" document.querySelector(".myclass"); // The same, but only gets the first one
document.querySelector("#myID"); // Select by ID
document.querySelectorAll("circle"); // All the circles
document.querySelector("circle.myclass"); // The first circle with a class of "myclass"
document.querySelectorAll("g.myclass > circle"); // All the circles that are immediate children of a group with a class of "myclass"
CSS selectors are powerful things these days (though still with some limitations), and a well crafted querySelector() can save a lot of DOM traversal in code.
How about browser support? What are the limitations you mention? These CSS type queries certainly seem more intuitive to someone knowing CSS, but before I switch to them I'd like to check for any downsides.
- "Group Clicking Gotchas"
"Let me start with this piece of advice: You'll probably *never* want to put events on the group itself."
I disagree with this advice. There's nothing wrong with putting event handlers on the group, or on the items within it, or on both. You just need to understand how events propagate within browsers; it's a slightly odd process, borne of a need to combine the old Internet Explorer way of handling them with the W3C standard way. In short, the things you need to know are these:
- "evt" will be the name of the event in the handler.
- "evt.target" will be the object that fired the event (i.e. the
object that was clicked on), not necessarily the one the handler is on.
- "this" will be the name of the object the handler is attached to.
Thanks. I missed that one and was using evt.currentTarget, which seemed iffy, before I decided not to put events in groups at all.
- The handler can call another function, passing the event, but also
passing other parameters.
- If a child object wants to consume an event and not pass it up to
its ancestors, you must call evt.stopPropagation()
Thanks. I didn't know about that one either.
I've put an example file up on my site which shows some of this at work:
http://peppertop.com/interactive_groups.svg
The whole file is a single group, but there are four onclick handlers (one on the group, one on each circle). Clicking a circle will change its colour randomly; clicking on the group (i.e. either lozenge shape) will change all three circles. There are also onmouseover and onmouseout handlers on the circles to thicken their borders when the mouse moves over them.
By putting a click handler on the group, and passing both "evt" and "this" you can get a reference to the object that was clicked ("evt.target") and the group itself ("this").
Other than demonstrating "this" and "if (e) e.stopPropagation();", is there a benefit to putting "onclick=change_all_colours();" on the group rather than on each losenge? I think the behavior would be identical, it would have simplified change_fill(), and would have required only one argument for change_fill().
I can see a benefit for my screw group, namely, that I wouldn't need to include the transparent cover. My screw group is somewhat atypical in that it should perform the identical action no matter what part of the group is clicked, hovered or unhovered, and there are no "blank" places in the group the way there are in your peppertop page in the area between the two losenges.
Let me give this some more thought, and thanks for another supremely instructive interactive SVG.
P.S. Did you see the message I posted a couple of weeks ago about making AJAX calls from SVG? I saw no feedback on it, so I'm not sure if it made it to the mailing list or not.
Yes. I had so much momentum on the stuff I was doing, that instead of exploring it I put it on my "later" pile. When I start making a back end for my apps, I'll study your AJAX methodology. Thanks for cluing me in about AJAX.
SteveT
Steve Litt May 2017 featured book: Twenty Eight Tales of Troubleshooting http://www.troubleshooters.com/28
On Tue, May 9, 2017 at 1:19 AM, Steve Litt <slitt@...2357...> wrote:
CSS selectors are powerful things these days (though still with some limitations), and a well crafted querySelector() can save a lot of DOM traversal in code.
How about browser support? What are the limitations you mention? These CSS type queries certainly seem more intuitive to someone knowing CSS, but before I switch to them I'd like to check for any downsides.
Support is excellent - all the way back to IE9 (and IE8, if you limit the choice of selectors).
http://caniuse.com/#search=querySelector
The limitations are purely those inherent in CSS selectors in general - e.g. that you can only select descendants of your chosen node, not its ancestors, and that there's no CSS selector for "the current node itself" ("this", if you will). None if this is likely to affect your use of them, and as a replacement for getElement(s)By... there are no real downsides.
By putting a click handler on the group, and passing both "evt" and
"this" you can get a reference to the object that was clicked ("evt.target") and the group itself ("this").
Other than demonstrating "this" and "if (e) e.stopPropagation();", is there a benefit to putting "onclick=change_all_colours();" on the group rather than on each losenge? I think the behavior would be identical, it would have simplified change_fill(), and would have required only one argument for change_fill().
No, it was purely demonstrative to show that you can put a handler on the <g> and use "this" to get a reference to the group.
Regards,
Mark
On Tue, 9 May 2017 09:45:43 +0100 Mark Crutch <markc@...2744...> wrote:
On Tue, May 9, 2017 at 1:19 AM, Steve Litt <slitt@...2357...> wrote:
CSS selectors are powerful things these days (though still with some limitations), and a well crafted querySelector() can save a lot of DOM traversal in code.
How about browser support? What are the limitations you mention? These CSS type queries certainly seem more intuitive to someone knowing CSS, but before I switch to them I'd like to check for any downsides.
Support is excellent - all the way back to IE9 (and IE8, if you limit the choice of selectors).
This caniuse.com seems like an excellent resource. Thanks!
It looks like .querySelector() works with major browsers back at least 5 years, and that's good enough. Actually, according to this page, my user of <embed/> is a bigger problem.
The limitations are purely those inherent in CSS selectors in general
- e.g. that you can only select descendants of your chosen node, not
its ancestors,
Sounds pretty good to me. If I wanted to cast a wider net, I'd do document.querySelector('.whatever') instead of myElement.querySelector('.whatever').
and that there's no CSS selector for "the current node itself" ("this", if you will). None if this is likely to affect your use of them, and as a replacement for getElement(s)By... there are no real downsides.
Thanks.
By putting a click handler on the group, and passing both "evt" and
"this" you can get a reference to the object that was clicked ("evt.target") and the group itself ("this").
Other than demonstrating "this" and "if (e) e.stopPropagation();", is there a benefit to putting "onclick=change_all_colours();" on the group rather than on each losenge? I think the behavior would be identical, it would have simplified change_fill(), and would have required only one argument for change_fill().
No, it was purely demonstrative to show that you can put a handler on the <g> and use "this" to get a reference to the group.
After considerable thought, my screwhead and LED objects will have the methods on the group, use "this" instead of e.currentTarget or a parent-looping use of e.target to find the group, and mygroup.querySelector('.screwhead') to find the subcomponent that will change colors. By doing this, I eliminate the transparent cover, which could become somewhat important when I'm making over 100 copies of these group objects.
When you clued me in to 'this', it opened a world of possibilities.
Thanks
SteveT
Steve Litt May 2017 featured book: Twenty Eight Tales of Troubleshooting http://www.troubleshooters.com/28
On Tue, May 9, 2017 at 7:57 PM, Steve Litt <slitt@...2357...> wrote:
The limitations are purely those inherent in CSS selectors in general
- e.g. that you can only select descendants of your chosen node, not
its ancestors,
Sounds pretty good to me. If I wanted to cast a wider net, I'd do document.querySelector('.whatever') instead of myElement.querySelector('.whatever').
The limitation of not being able to select ancestors can sometimes be a problem if you've got an event handler. Consider this example of a few groups within groups:
<g id="myParent"> <g onclick="clicked(this);"> <g class="inner"> <circle id="circle1" ... /> </g> </g>
<g onclick="clicked(this);"> <g class="inner"> <circle id="circle2"... /> </g> </g> </g>
In your event handler you get a handle to the clicked group, but if you wanted to get a handle to the parent element ("myParent"), there's no way to use querySelector() to get to it. E.g.
function clicked(oGroup) { var c = oGroup.querySelector(".inner > circle"); // Gets the circle - selecting into the tree is fine var c = oGroup.querySelector("g > circle"); // Does the same var c = oGroup.querySelector("circle"); // So does this (in this case) var c = oGroup.querySelector("g circle"); // As does this (in this case), but a little less efficiently
// If I want to get the parent group, this would be a nice option... var g = oGroup.querySelector("this < g"); // There's no "this" in CSS, and no "<" as a parent selector. This will fail.
// Instead I end up doing this... var g = oGroup.parentNode; }
In this simple example that's not too bad, but when you start getting more complex DOM structures, you can end up having to maintain code that has "this.parentNode.parentNode.nextElementSibling.parentNode" and similarly fragile structures.
So yes, you can use document.querySelector() to select from the entire document, or node.querySelector() to restrict the selection, but depending on your "starting point" it you might not have the luxury of choice. It's generally a lot easier going down into the tree than walking back up it.
When you clued me in to 'this', it opened a world of possibilities.
Just be a little careful with "this", as it can change depending on how a function is called. It's often worth logging it out to make sure it actually represents the thing you think it does. This can be a particular problem if you start trying to do anything asynchronously, using setTimeout() or similar.
Regards,
Mark
participants (2)
-
Mark Crutch
-
Steve Litt