So I've got my canvas and my paths:
var paper1 = Raphael(10, 50, 960, 560);
var mapShape1 = paper1.path("M339.098,175.503c0,0-55.555,58.823-16.34,75.163s227.451,49.02,227.451,49.02s67.321-25.49,47.713-50.98s-71.896-78.432-71.896-78.432L339.098,175.503z");
var mapShape2 = paper1.path("M548.902,306.876c0,0-209.15-32.026-228.758-46.405s-27.451-27.451-20.262-42.484s26.797-44.444,26.797-44.444l-41.83-86.928l-76.471,77.125c0,0-25.49,169.935,48.366,171.242s292.157-4.575,292.157-4.575V306.876z");
var mapShape3 = paper1.path("M296.614,86.614l38.562,83.66l194.771-7.843l75.817,81.7c0,0,130.066-84.967,73.203-118.301S503.15,48.706,463.935,51.974S296.614,86.614,296.614,86.614z");
And I style them like this: (I believe this could be improved, is there a way to do all the paths at once???)
function style1(shape){
shape.attr({
"fill": "#33CCFF",
"stroke": "000000",
"stroke-width": "5"
});
}
style1(mapShape1);
style1(mapShape2);
style1(mapShape3);
But my question is how to have a single hover function work on all the paths, I've got this:
mapShape1.hover(
function(){
this.animate({
"fill": "#FF3300"
}, 500);
},
function(){
this.animate({
"fill": "#33CCFF"
}, 500)
}
);
But it only works with one shape at a time, I want to do
$(mapShape1, mapShape2, mapShape3).hover(...
but that doesn't work. What am I missing?
As said by lib3d, you should use a Set. However, rather than using forEach
to loop over the set contents and apply attributes/functionality, shared attributes/functionality can be added on the set itself, which will apply it on the contents of the set. More on that later, first look at how you can create sets.
There are two ways to create a set and add elements to it: explicit and implicit.
This means that you manage the set yourself, and add elements to the set yourself`
var paper, shapeA, shapeB, shapeC, elementSet;
paper = Raphael(10, 50, 960, 560);
elementSet = paper.set();
shapeA = paper.path("M339.098,175.503c0,0-55.555,58.823-16.34,75.163s227.451,49.02,227.451,49.02s67.321-25.49,47.713-50.98s-71.896-78.432-71.896-78.432L339.098,175.503z");
shapeB = paper.path("M548.902,306.876c0,0-209.15-32.026-228.758-46.405s-27.451-27.451-20.262-42.484s26.797-44.444,26.797-44.444l-41.83-86.928l-76.471,77.125c0,0-25.49,169.935,48.366,171.242s292.157-4.575,292.157-4.575V306.876z");
shapeC = paper.path("M296.614,86.614l38.562,83.66l194.771-7.843l75.817,81.7c0,0,130.066-84.967,73.203-118.301S503.15,48.706,463.935,51.974S296.614,86.614,296.614,86.614z");
// now add A and C to the set, as well as a rectangle
elementSet.push(
shapeA,
shapeC,
paper.rect(10, 10, 10, 10, 2)
);
This way you are in full control over what enters the set and what not.
You also have the ability to mark start and endpoints when drawing elements. Any element that is drawn between the start and endpoint, is added to the set.
var paper, shapA, shapeB, shapeC, elementSet;
paper = Raphael(10, 50, 960, 560);
paper.setStart();
shapeA = paper.path("M339.098,175.503c0,0-55.555,58.823-16.34,75.163s227.451,49.02,227.451,49.02s67.321-25.49,47.713-50.98s-71.896-78.432-71.896-78.432L339.098,175.503z");
shapeB = paper.path("M548.902,306.876c0,0-209.15-32.026-228.758-46.405s-27.451-27.451-20.262-42.484s26.797-44.444,26.797-44.444l-41.83-86.928l-76.471,77.125c0,0-25.49,169.935,48.366,171.242s292.157-4.575,292.157-4.575V306.876z");
shapeC = paper.path("M296.614,86.614l38.562,83.66l194.771-7.843l75.817,81.7c0,0,130.066-84.967,73.203-118.301S503.15,48.706,463.935,51.974S296.614,86.614,296.614,86.614z");
paper.rect(10, 10, 10, 10, 2);
elementSet = paper.setFinish();
The variable elementSet now contains the shapes A, B and C as well as a rectangle.
Personally I would advice to always use the explicit method. That way you have 100% control over what enters your set and what not. Also, I find the setStart() and setFinish() to be named backwards, we're "starting" with a "set", we're not "setting" a "start". This might be obvious if you now the intents, but that is exactly the danger of ambiguous naming - the next dev might not know and assume something different.
For an application we created, we had to draw, remove, update and reposition complex groups of elements. In order to achieve this, we made heavy use of sets. Bar the fact that sets allow you to apply attributes on every element in a set, a set also allows you to use it as a DTO.
For instance the following works:
var elementSet = paper.set();
elementSet.push(elemA, elemB, elemC);
elementSet.myApp.someDTO = {
property: value,
something: else
};
I tend to use the myApp
as a namespace, for consistency and clarity.
The beauty of it is that even if someDTO contains Raphael elements, anything you apply on the set, will not be applied on the elements in the DTO. This makes it really usable to pass around context, coordinates, etc should you need to.
Now back to the benefit of using sets. Let us review your usecase here: you want to apply attributes and hover to an arbitrary amount of paths.
If we create a set as in the explicit example above, we end up with the following:
var paper, elementSet;
paper = Raphael(10, 50, 960, 560);
elementSet = paper.set();
elementSet.push(
paper.path("M339.098,175.503c0,0-55.555,58.823-16.34,75.163s227.451,49.02,227.451,49.02s67.321-25.49,47.713-50.98s-71.896-78.432-71.896-78.432L339.098,175.503z"),
paper.path("M548.902,306.876c0,0-209.15-32.026-228.758-46.405s-27.451-27.451-20.262-42.484s26.797-44.444,26.797-44.444l-41.83-86.928l-76.471,77.125c0,0-25.49,169.935,48.366,171.242s292.157-4.575,292.157-4.575V306.876z"),
paper.path("M296.614,86.614l38.562,83.66l194.771-7.843l75.817,81.7c0,0,130.066-84.967,73.203-118.301S503.15,48.706,463.935,51.974S296.614,86.614,296.614,86.614z"),
);
Now apply the styling on the set:
elementSet.attr({
fill: '#33CCFF',
stroke: '#000000',
'stroke-width': 5
});
And then add the hover:
elementSet.hover(
function(){
this.animate({
"fill": "#FF3300"
}, 500);
},
function(){
this.animate({
"fill": "#33CCFF"
}, 500)
}
);
Sets also support chaining, as elements do:
elementSet.push(
/* elements */
).attr({
/* attributes */
}).hover(
/* hover fn's
);
To view the final result, there's a fiddle here
Should you want to apply the onhover highlight to all elements, you could just apply the attributes on the set again:
onMouseOver: function () {
elementSet.animate({
fill: '#FF3300'
}, 500);
};
onMouseOut: function () {
elementSet.animate({
fill: '#33CCFF'
}, 500);
};
elementSet.hover(onMouseOver, onMouseOut);
A fiddle to view this can be found here
In order to be able to bind hover functionality through jQuery, one must access the nodes of the elements. The elements themselves are not DOM nodes, rather Raphael objects. Through using the element.node
one can use jquery on that node to add behaviour. My personal experience is that this works decent, however you never ever want to modify the node through jquery as that can lead to really unexpected behavior.
Raphael provides about all the functionality you need, using jquery shouldn't be needed.
Why not give your shapes a class, and have jquery select the class?
You could do the following:
function style1(shape){
shape.attr({
"fill": "#33CCFF",
"stroke": "000000",
"stroke-width": "5",
"class": '.js-path-hover'
});
}
style1(mapShape1);
style1(mapShape2);
style1(mapShape3);
Then your hover event could be set up as follows:
$('.js-path-hover').on('hover', functionNameHere);
If raphael does not give you access to write a class onto these svg objects then you could use D3 to select them all and add a class to them. I've found combining D3, Raphael and jQuery to be really powerful. The only issue is you need to keep track of each of their limitations.
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