Raphaeljs and Internet Explorer, problem when clicking an element - javascript

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!

Related

How to reset a svg coordinates on clicking a second time in Snap.svg

I want to create an icon that turns into a close button when clicking on it and on clicking again on the button, it turns into the original icon again. So far I have managed to do the first part of the code. I know that I can use an if else condition but I don't know what to check for. If you are looking for a jsfiddle, I'm sorry since there is no cdn for snap.svg yet.
<span class="bars" data-icon-name="bars">
<svg id="svg"></svg>
</span>
<script type="text/javascript">
var Paper = Snap('#svg');
var linepaths = Paper.line(20, 20, 100, 20);
var linepaths1 = Paper.line(20, 33, 100, 33);
var linepaths3 = Paper.line(20, 46, 100, 46);
Paper.attr({
stroke:'#fff',
strokeWidth: 7
});
Paper.click(
function (){
linepaths.animate({x1:20,y1:20,x2:75,y2:75},500);
linepaths1.animate({x1:20,y1:75,x2:75,y2:20},500);
linepaths3.animate({x1:0,y1:0,x2:0,y2:0},1);
linepaths3.attr({
stroke:'#2ecc71',
strokeWidth: 7
});
});
</script>
You could just do a basic toggle I guess, it depends how you want it to work a bit..
Paper.click( function() { if( toggle ) {
buttonOn();
toggle = false;
} else {
buttonOff();
toggle = true; }
} );
function buttonOn() {
linepaths.animate({x1:20,y1:20,x2:75,y2:75},500);
linepaths1.animate({x1:20,y1:75,x2:75,y2:20},500);
linepaths3.animate({x1:0,y1:0,x2:0,y2:0},1);
linepaths3.attr({
stroke:'#2ecc71',
strokeWidth: 7
});
};
function buttonOff() {
linepaths.animate({x1:20,y1:20,x2:100,y2:20},500);
linepaths1.animate({x1:20,y1:33,x2:100,y2:33},500);
linepaths3.animate({x1:20,y1:46,x2:100,y2:46},1);
linepaths3.attr({
stroke:'#2ecc71',
strokeWidth: 7
});
};
jsfiddle http://jsfiddle.net/2UZr2/5/

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

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!

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;
}

Stop animation and reset position of an element in Raphaƫl.js

I have this simple animation that moves from side to side, Im trying to create a reset button so that the animation stops and gets back to default, but I cant seem to get i right.
The library I am using is Raphael, but I'm almost sure that with a simple javascript I can reset the values inside the function.
Body onload init function
function init() {
paper = Raphael("loadSVG");
var bg = paper.rect( 0, 0, "240px", "90px", 0 );
bg.attr( {fill: "#f3f3ff"} );
rect1 = paper.rect(150, 20, 50, 50);
rect1.attr( {fill: "#ffaaaa", "stroke-width": 3} );
}
The animation function
function moveRect1() {
if( xEnd == 150 )
xEnd = 50;
else
xEnd = 150;
rect1.animate( {x: xEnd}, 1000, "Sine", function (){
moveRect1();
});
}
and the stop button that don't work :)
function stopsvgRect1() {
rect1.stop();
}
You need to bind the stop button to the function you've created.
For example like this:
document.querySelector('#stop').onclick = function() {
stopsvgRect1();
};
Open this fiddle to see it in action.
edit:
As the position should also be resetted, you can choose Raphael.transform() for this action.
Open my updated fiddle to see how it works.

How to add raphaeljs object in <div> tag

$(document).ready(function(){
$("#btnAO").live("click", function(){
$("#canvasdiv").append("<div id='id1' width='50px' height='50px'></div>");
$("#id1").append(new Raphael(document.getElementById('canvasdiv'), 900, 600).rect(30, 50, 80, 100).attr({
fill : "blue",
stroke : "black",
strokeWidth : 0,
r : 5
}));
});
});
i have tried this its add Raphael object in but it wont display on screen
Raphael renders into the container that you give it as the first argument. The return value is a Raphael paper object which you use for rendering. In short, just cut away $("#id1").append and it shows up.
$(document).ready(function(){
$("#btnAO").live("click", function(){
$("#canvasdiv").append("<div id='id1' width='50px' height='50px'></div>");
var paper = new Raphael(document.getElementById('canvasdiv'), 900, 600);
paper.rect(30, 50, 80, 100).attr({
fill : "blue",
stroke : "black",
strokeWidth : 0,
r : 5
});
});
});
Further more, since you're using jQuery anyway, you might want to replace document.getElementById('canvasdiv') with $('#canvasdiv').get(0) for consistency.
new keyword is not needed
var paper = Raphael(document.querySelector(target_css_selection_str), svg_width_int, svg_height_int);
Since you ask about what it returns. It returns a Paper object, which holds a reference to the new SVG element that it just built, via a property it calls "canvas".
...you should approve #Supr as the right answer btw, I am just adding a 2 cents.

Categories