the red circle's click event is invoked twice in IE8 - javascript

when I click the red circle, the window will popup a alert once.
It works fine in firefox and chrome, but in ie8, it's popup alerts twice.
How could I fix it?
please see my code in the following:
Raphael("world", 1000, 400, function () {
var r = this;
r.rect(0, 0, 1000, 400, 0).attr({
stroke: "none",
fill: "0-#9bb7cb-#adc8da"
});
var click = function(){
alert(this.type);
};
r.setStart();
var hue = Math.random();
for (var country in worldmap.shapes) {
r.path(worldmap.shapes[country]).attr({stroke: "#ccc6ae", fill: "#f0efeb", "stroke-opacity": 0.25});
}
var dot = r.circle(772.9870633333333, 166.90446666666668).attr({
title: "Point",
fill: "red",
stroke: "#fff",
"stroke-width": 2,
r: 5
});
var world = r.setFinish();
world.click(click);
});

I've had this problem a few time. Solved it by using a mouse up event instead of a click event. IE sucks.

I find a way to fix this issue, replace the Raphael Set Object with a array Object to push all Rahpael Elements Object, then loop the array to add click event for each Element.
see the following code:
var set = [];
// r.setStart();
for (var country in worldmap.shapes) {
var element = r.path(worldmap.shapes[country]).attr({stroke: "#ccc6ae", fill: "#f0efeb", "stroke-opacity": 0.25});
set.push(element);
}
var dot = r.circle(772.9870633333333, 160.90446666666668 , 5).attr({
title: "Point",
fill: "red",
stroke: "#fff",
"stroke-width": 2
});
set.push(dot);
for(var i = 0; i < set.length; i++){
var element = set[i];
element.click(click);
}
// var world = r.setFinish();
// world.click(click);

I had a similar problem in Firefox, although what I was doing was completely different.
The way I solved it was to prevent the event from firing twice in too short of an interval.
I would use this function:
function rateLimit(func) {
var lastcall = func.lastcall || 0,
now = new Date().getTime();
if( now-lastcall < 250) return false;
func.lastcall = now;
return true;
}
Then in the function I want to limit from firing too often, I can do this:
if( !rateLimit(arguments.callee)) return false;
However, you might have a small issue if you are using alert(), since that will completely block execution and the second run will still fire. I would strongly suggest using console.log() instead of alert() to keep track of values, as this will avoid interrupting the flow of the program (especially when you get into asynchronous stuff, you can get real mysteries if you stop things with an alert)
Hope this helps!

Related

Raphaeljs path id inside a set attributes

I'm working on a map with Raphaeljs and I have the paths inside a set because I want all of them to have the same attributes and all is good but now I would like to set an id to each path and I did it to one but the attributes got lost, so my question is...Is there a way to have an path.id inside a set with the set attributes? Thanks
Here is the file http://jsfiddle.net/tLSpv/2/
var paper = Raphael(0,0,540, 615);
var newmexico = paper.set();
newmexico.push(
paper.path("M343.249,11.503l-1.658,3.554l-0.474,16.822c0,0-3.554-0.711-5.449-0.711 s-5.686,3.554-5.449,4.265c0.237,0.711-1.895,13.268-1.895,14.215s3.554,4.502,3.317,5.449c-0.237,0.948-0.711,3.554-0.474,4.502 c0.237,0.948,0.711,7.345,0.474,9.477c-0.237,2.132,0.948,11.846-1.658,13.268h42.883v5.449h38.855h37.197V73.103l1.303-1.303V1.239 L340.643,1.18v4.4L343.249,11.503z").node.id = 'colfax';
).attr({
fill: '#F7F0EA',
stroke: '#006599',
'stroke-width': 1,
cursor: 'pointer'
})
.hover(function () {
this.animate({fill: '#006599'}, 300);
},
function () {
this.animate({fill: '#F7F0EA'}, 300)
}
);
Here is a suggestion. Add Raphaels data() function to your on top of your attribute list.
Exp:
for (var i = 0, i < 5, i++) {
paper.circle(10 + 15 * i, 10, 10)
.attr({fill: "#000"})
.data("i", i)
.click(function () {
alert(this.data("i"));
});
}
This little example is taken from here. data Adds or retrieves given value associated with given key. In this case "i" is a key and i is the value.
This is how I assign ids to my Raphael objects. Good Luck

How to prevent loss hover in Raphael?

I'm developing some page when I use Raphael liblary to draw some items.
my App
So my problem is in that when I'm moving to some rect it growing up but when my mouse is on text which is positioning on my rect, it loss his hover. You can see it on my app example.
var paper = new Raphael(document.getElementById('holder'), 500, object.length * 100);
drawLine(paper, aType.length, bType.length, cType.length, cellSize, padding);
process = function(i,label)
{
txt = paper.text(390,((i+1)* cellSize) - 10,label.devRepo)
.attr({ stroke: "none", opacity: 0, "font-size": 20});
var a = paper.rect(200, ((i+1)* cellSize) - 25, rectWidth, rectHeight)
.hover(function()
{
this.animate({ transform : "s2"}, 1000, "elastic");
this.prev.animate({opacity: 1}, 500, "elastic");
this.next.attr({"font-size" : 30});
},
function()
{
this.animate({ transform : "s1" }, 1000, "elastic");
this.prev.animate({opacity: 0}, 500);
this.next.attr({"font-size" : 15});
});
}
I have tried e.preventDefault(); on hover of this.next and some other solutions but it's doesn't work.
Any help would be appreciated.
Most people will suggest you place a transparent rectangle over the box and the labels and attach the hover functions to that instead. (If memory serves, you have to make the opacity 0.01 instead of 0 to prevent the object from losing its attached events.) This works fine, but I don't love this solution; it feels hacky and clutters the page with unnecessary objects.
Instead, I recommend this: Remove the second function from the hover, making it functionally a mouseover function only. Before you draw any of the rectangles and labels, make a rectangular "mat" the size of the paper. Then, attach the function that minimizes the label as a mouseover on the mat. In other words, you're changing the trigger from mousing out of the box to mousing over the area outside of it.
I left a tiny bit of opacity and color on the mat to be sure it's working. You can just change the color to your background color.
var mat = paper.rect(0, 0, paper.width, paper.height).attr({fill: "#F00", opacity: 0.1});
Now, you want to make a container for all the rectangles so you can loop through them to see which need to be minimized. I made an object called "rectangles" that contains the objects we're concerned with. Then:
mat.mouseover(function () {
for (var c = 0; c < rectangles.length; c += 1) {
//some measure to tell if rectangle is presently expanded
if (rectangles[c].next.attr("font-size")) {
rectangles[c].animate({
transform : "s1"
}, 1000, "elastic");
rectangles[c].prev.animate({opacity: 0}, 500);
rectangles[c].next.attr({"font-size" : 15});
}
}
});
Then I just removed the mouseout function from the individual rectangles.
jsBin
To be clear, this will have some downsides: If people run the mouse around really fast, they can expand several rectangles at the same time. This is remedied as soon as the mouse touches the mat. I think the functionality looks pretty nice. But the invisible mats is always an option.
I wrote a small extension to Raphael - called hoverInBounds - that resolves this limitation.
Demo: http://jsfiddle.net/amustill/Bh276/1
Raphael.el.hoverInBounds = function(inFunc, outFunc) {
var inBounds = false;
// Mouseover function. Only execute if `inBounds` is false.
this.mouseover(function() {
if (!inBounds) {
inBounds = true;
inFunc.call(this);
}
});
// Mouseout function
this.mouseout(function(e) {
var x = e.offsetX || e.clientX,
y = e.offsetY || e.clientY;
// Return `false` if we're still inside the element's bounds
if (this.isPointInside(x, y)) return false;
inBounds = false;
outFunc.call(this);
});
return this;
}

In raphaeljs, is there a way to place text within an object to keep rollover state

In creating a svg map using raphael js where I have hover states. How ever I am trying to write the country names onto the map. The problem I am facing is the names become their own object and block the country so I loose hover and click when the cursor is directly over the text. Is there a way where I can draw the text and not have it block the map. I've tried set() but with no sucess.
Thanks,
What I have below doesn't have the text() or print() included:
var r = Raphael('map', 1450, 2180);
arr = new Array();
for (var country in paths) {
var obj = r.path(paths[country].path);
countryName = paths[country].name;
color = paths[country].color;
scolor = paths[country].stroke;
obj.attr({fill:color,stroke:scolor,
'stroke-width': 1,
'stroke-linejoin': 'round'});
arr[obj.id] = country;
obj
.hover(function(){
console.log(arr[this.id]);
this.animate({
fill: paths[arr[this.id]].hover
}, 300);
}, function(){
this.animate({
fill: paths[arr[this.id]].color
}, 300);
})
});
Try setting the pointer-events attribute to none for the text elements.
Documentation:
http://www.w3.org/TR/SVG/interact.html#PointerEventsProperty

Raphaeljs and Internet Explorer, problem when clicking an element

I have the following piece of code in javascript that basically hide or show a Raphaeljs set when I click on it. It works perfectly well under Google Chrome, FireFox and Safari, but not at all under Internet Explorer.
var paper = Raphael(document.getElementById('ADiv'), 450, 490);
var group = paper.set();
var toxicRect = paper.rect(0, 0, 120, 60, 10 );
toxicRect.attr({"stroke-width": 1,
"stroke" : "#3083BE",
"fill" : "#D1DFE9"});
group.push( toxicRect );
var toxicRectText = paper.text(60, 25, "Toxic in air\nthrough inhalation");
toxicRectText.attr({"font-size": 12 });
group.push( toxicRectText );
var toxicToConcentrationPath = paper.path("M60 60L60 80")
toxicToConcentrationPath.attr({"stroke-width": 1,
"stroke" : "#3083BE"});
group.push( toxicToConcentrationPath );
var concentrationRect = paper.rect(0, 80, 120, 60, 10 );
concentrationRect.attr({"stroke-width": 1,
"stroke" : "#3083BE",
"fill" : "#D1DFE9"});
group.push(concentrationRect);
var conRectText = paper.text( 60, 105, "Is concentration\n> TLV/TWA");
conRectText.attr({"font-size": 12});
group.push(conRectText);
var conRectTextYes = paper.text(50, 135, "Yes / ");
conRectTextYes.attr({"font-size": 12,
"font-style": "italic"});
group.push(conRectTextYes);
var conRectTextNo = paper.text(75, 135, "No");
conRectTextNo.attr({"font-size": 12,
"font-style": "italic"});
group.push(conRectTextNo);
var monitorConcentrationGroup = paper.set();
var monitorConcentrationRect = paper.rect(140, 95, 60, 30, 10 );
monitorConcentrationRect.attr({"stroke-width": 1,
"stroke" : "#3083BE",
"fill" : "#D1DFE9"});
monitorConcentrationGroup.push(monitorConcentrationRect);
var monitorConcentrationRectText = paper.text(170, 115, "Monitor");
monitorConcentrationRectText.attr({"font-size": 12});
monitorConcentrationGroup.push(monitorConcentrationRectText);
var concentrationToMonitorPath = paper.path("M120 110L140 110")
concentrationToMonitorPath.attr({"stroke-width": 1,
"stroke" : "#3083BE"});
monitorConcentrationGroup.push(concentrationToMonitorPath);
monitorConcentrationGroup.hide();
//Actions when clicking on decisions
conRectTextYes.node.onclick = function () {
monitorConcentrationGroup.hide();
};
conRectTextNo.node.onclick = function () {
monitorConcentrationGroup.show();
};
Anyone has any ideas? You can test it at http://raphaeljs.com/playground.html by making a cut and paste and omitting the first line of the script. Clicking the "No" should make a box appears, at least in Chrome, but not in IE...
Thank you!
Raphael uses VML to render shapes in IE8, specifically the VML textpath element in your example. However, IE8 does not fire mouse events for that element. You could go up the node tree and attach your event handler to the shape element that contains the textpath but, even then, the active-area only consists of the pixels that make up the text, so it's very difficult to click.
A better solution would be to add a transparent rectangle behind the text and attach your event handler to that as well:
...
// Make the rectangle slightly larger and offset it
// from the text coordinates so that it covers the text.
var conRectTextNoContainer = paper.rect(75 - 8, 135 - 9, 17, 14);
// Give the rectangle a fill (any color will do) and
// set its opacity to and stroke-width to make it invisible
conRectTextNoContainer.attr({
fill: '#000000',
'fill-opacity': 0,
'stroke-width': 0
});
group.push(conRectTextNoContainer);
var conRectTextNo = paper.text(75, 135, "No");
conRectTextNo.attr({
"font-size": 12,
"font-style": "italic"
});
group.push(conRectTextNo);
...
var conRectTextNoNode = conRectTextNo.node;
if (conRectTextNoNode.tagName === 'textpath') {
// We're in IE8, attach the handler to the parentNode
conRectTextNoNode = conRectTextNo.node.parentNode;
}
conRectTextNoContainer.node.onclick = (
conRectTextNoNode.onclick = function () {
monitorConcentrationGroup.show();
}
);
...
Working Demo: http://jsfiddle.net/brianpeiris/8CZ8G/show/ (Editable via: http://jsfiddle.net/brianpeiris/8CZ8G/)
roughly similar to another question and I will post the same answer:
onmousedown worked for me in IE 7,8, and 9
st[0].onclick = function () {
myFunc(dataObj);
st.toFront();
R.safari();
};
st[0].onmousedown = function () {
myFunc(dataObj);
st.toFront();
R.safari();
};
I tried some other methods as well, abstracting the function to a variable but it didnt work. In my case I cannot add a rectangle to the display and have people click on this, it was a poor solution for several reasons.
Hope this helps!

Events work in Firefox/Chrome but fail in IE

I've just built an SVG map of New Zealand for use with the excellent javascript library Raphael, but unfortunately have stumbled upon what I can only imagine is a bug or syntactic variation in IE's javascript interpreter.
In Firefox and other browsers the onlick and onmouseover events work perfectly - however they do not fire in IE (tested in IE 7). Unfortunately there is no javascript error to help me debug this, so I can only assume IE handles these events in some fundamentally different way.
<script type="text/javascript" charset="utf-8">
window.onload = function() {
var R = Raphael("paper", 450, 600);
var attr = {
fill: "#3f3f40",
stroke: "#666",
"stroke-width": 1,
"stroke-linejoin": "round"
};
var nz = {};
nz.northland = R.path(attr, "M 193.34222,3.7847503 C 194.65463");
// SVG data stripped for sake of brevity
var current = null;
for (var region in nz) {
nz[region].color = Raphael.getColor();
(function(rg, region) {
rg[0].style.cursor = "pointer";
rg[0].onmouseover = function() {
current && nz[current].animate({ fill: "#3f3f40", stroke: "#666" }, 500) && (document.getElementById(current).style.display = "");
rg.animate({ fill: rg.color, stroke: "#ccc" }, 500);
rg.toFront();
R.safari();
document.getElementById(region).style.display = "block";
current = region;
};
rg[0].onclick = function() {
alert("IE never gets this far.");
//window.location.href = "my-page.aspx?District=" + region;
};
rg[0].onmouseout = function() {
rg.animate({ fill: "#3f3f40", stroke: "#666" }, 500);
};
if (region == "northland") {
rg[0].onmouseover();
}
})(nz[region], region);
}
};
</script>
Many thanks :)
The fix appears to be using the onmousedown event instead of onclick.
Changing:
rg[0].onclick = function() {
alert("IE never gets this far, but Firefox is happy.");
};
to
rg[0].onmousedown = function() {
alert("This works in IE and Firefox.");
};
resolved the issue. Thanks for everyone's input - got there in the end. If anyone actually knows why IE doesn't like onclick, I'd be interested to hear!
Have you tried attaching the events?
if (rg[0].attachEvent)
rg[0].attachEvent("onclick", function(){ /* IE */ });
else
rg[0].addEventListener("click", function(){ /* other */ }, false);
IE is not exactly known for working correctly. It would help if you mentioned which IE version you are using.
Generally an abstraction framework, like jquery or prototype is your best bet. They handle the browser differences for you. Also, you can subscribe to your events at a higher level... it's less expensive in the browser to subscribe to the mousemove/click, and determine what you're over, or clicking on from the event bubble than to many objects.
Joel Potter mentioned the subscriber model, using the dom and IE methods, which is better practice than the method assignment is.

Categories