I am using D3 JavaScript library to construct an SVG diagram and assign mouse events to them. The SVG diagram can be complex, made with many arbitrary path elements. I have been struggling to figure out a way to determine all the nearby elements on a mouseover event. So, for example, when the mouse cursor moves over the diagram I can determine all the component SVG elements within a radius of N pixels from the cursor.
I am completely stumped on how to tackle this. I am not even sure if there is an API that allows me determine if an SVG constituent element is within a bounding region?
Any hints on how to tackle this would be most appreciated.
Preface: @Duopixel's comment on your original post is great -- I didn't know that method exists. However, it looks like it only supports a rectangular region per the MSDN docs, whereas it seems more intuitive to have a circle around the mouse.
A couple of other techniques:
Use an invisible, thick stroke that "expands" your element, if you're not otherwise using the stroke for styling, or draw a transparent line/shape on top of the element that is some amount larger. Listen to the 'click' event for these elements.
Do a pairwise comparison of every element with that of the mouse's click radius. I made an example here: http://jsfiddle.net/AkPN2/5/. I only implemented circles because they were easy -- you'd need to add the geometry for rectangles.
I ended up using the method in SVGEdit suggested by DuoPixel. It uses other SVGEdit method defined in svgcanvas.js so it's not really meant as a standalone. Here's a working link to DuoPixel method suggested in the comments: https://github.com/duopixel/Method-Draw/blob/master/method-draw/src/svgcanvas.js#L600
With the specific implementation in case the link changes:
// This method sends back an array or a NodeList full of elements that
// intersect the multi-select rubber-band-box on the current_layer only.
//
// Since the only browser that supports the SVG DOM getIntersectionList is Opera,
// we need to provide an implementation here. We brute-force it for now.
//
// Reference:
// Firefox does not implement getIntersectionList(), see https://bugzilla.mozilla.org/show_bug.cgi?id=501421
// Webkit does not implement getIntersectionList(), see https://bugs.webkit.org/show_bug.cgi?id=11274
var getIntersectionList = this.getIntersectionList = function(rect) {
if (rubberBox == null) { return null; }
var parent = current_group || getCurrentDrawing().getCurrentLayer();
if(!curBBoxes.length) {
// Cache all bboxes
curBBoxes = getVisibleElementsAndBBoxes(parent);
}
var resultList = null;
try {
resultList = parent.getIntersectionList(rect, null);
} catch(e) { }
if (resultList == null || typeof(resultList.item) != "function") {
resultList = [];
if(!rect) {
var rubberBBox = rubberBox.getBBox();
var bb = {};
for(var o in rubberBBox) {
bb[o] = rubberBBox[o] / current_zoom;
}
rubberBBox = bb;
} else {
var rubberBBox = rect;
}
var i = curBBoxes.length;
while (i--) {
if(!rubberBBox.width || !rubberBBox.width) continue;
if (svgedit.math.rectsIntersect(rubberBBox, curBBoxes[i].bbox)) {
resultList.push(curBBoxes[i].elem);
}
}
}
// addToSelection expects an array, but it's ok to pass a NodeList
// because using square-bracket notation is allowed:
// http://www.w3.org/TR/DOM-Level-2-Core/ecma-script-binding.html
return resultList;
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With