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.4
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.
$(mapShape1, mapShape2, mapShape3).live('hover', function() { });
Live should work better with the dynamic elements added to the DOM hover doesn't work with elements created after, and 'on' only works with created dom elemetns with a 3rd parameter specified.
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.
The shortest way would be to have your paths being pushed in a set and then use forEach on your set to style each path.
You would then bind hover handlers the same way, with handlers being first referenced as closure variables.