Skip to content

Instantly share code, notes, and snippets.

@alojzije
Last active March 24, 2024 19:36
Show Gist options
  • Save alojzije/11127839 to your computer and use it in GitHub Desktop.
Save alojzije/11127839 to your computer and use it in GitHub Desktop.
Connect two elements / draw a path between two elements with SVG path (using jQuery)

connect html elements with SVG path

Gist contains a javaScript file svgDraw.js for connecting any two html elements with an SVG path in a pipe-like fashion. It connects the bottom-middle point of the "higher" element with the top-middle point ot the "lower" element. preview

Also, index.html, and style.css are provided for demonstration purposes.

==

USAGE:

In odrer to connect any two elements, they need an ID. For the purposes of this demonstration, we shall id them as "Mary" and "Tom". Style with CSS as you normally would.

 <div id="Mary"></div>
 <p id="Tom"></p>

Next, (also in your .html) we need to add a path with which to connect our Mary and Tom elements. We do that by appending a child <path> element to <svg>. Note that they also have unique IDs, "myNewPath" and "svg1", respectively .

<svg id="svg1" width="0" height="0" >
       <path
            id="myNewPath"
            d="M0 0"             
            stroke-width="0.3em"
            style="stroke:#555; fill:none;  "/>
</svg>

And now for the fun part. The actual connecting! In svgDraw.js locate function connectAll() and add your connection like so:

function connectAll() {
    // connect all the paths you want!
    connectElements($("#svg1"), $("#myNewPath"), $("#Mary"),  $("#Tom"));
    ...
    connectElements($("#svg1"), $("#someOtherPath"), $("#purple"), $("#teal")  );
    connectElements($("#svg1"), $("#yetAnotherPath"), $("#Tom"), $("#teal")  );
}

aand... you're done!
You can repeat these steps any number of times connecting any number of different elements ( or connecting any one element with any number of different elements, for that matter :))
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Connect divs with SVG</title>
<link rel="stylesheet" type="text/css" href="style.css">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
</head>
<body>
<div id="svgContainer" style="margin: 50px 50px;">
<svg id="svg1" width="0" height="0" >
<path
id="path1"
d="M0 0"
stroke="#000"
fill="none"
stroke-width="12px";/>
<path
id="path2"
d="M0 0"
stroke="#000"
fill="none"
stroke-width="12px";/>
<path
id="path3"
d="M0 0"
stroke-width="8px"
style="stroke:#000; fill:none;"/>
<path
id="path4"
d="M0 0"
stroke-width="12px"
style="stroke:#000; fill:none; stroke-width: 12px;" />
<path
id="path5"
d="M0 0"
stroke-width="10px"
style="stroke:#000; fill:none;"/>
<path
id="path6"
d="M0 0"
stroke-width="10px"
style="stroke:#000; fill:none;"/>
</svg>
</div>
<div id= "outer">
<div id="teal"></div>
<div id="red"></div>
<div id="green"></div>
<div id="purple"></div>
<div id="orange"></div>
<div id="aqua"></div>
</div>
<script src="svgDraw.js"></script>
</body>
</html>
body{ background-color:gray; }
#svgContainer {
z-index: -10;
position:absolute;
background-color:silver;
opacity: 0.5;
}
div{ opacity: 0.6; }
#outer{
margin:0 auto;
width: 80%;
}
#teal{
width: 6em;
height: 6em;
background-color:teal;
margin-left: 10%;
}
#orange{
height: 4em;
width: 35%;
padding: 2em 8em;
margin-left: 8em;
margin-top: 6em;
background-color: orange;
}
#red{
width:6em;
height: 4em;
margin-left: 30%;
padding:4em 3em;
background-color:red;
}
#aqua{
width: 5em;
height: 5em;
margin-left:15%;
background-color:aqua;
}
#purple{
width: 15em;
height: 5em;
background-color:purple;
}
#green{
width: 5em;
height: 7em;
margin-top: 2em;
margin-left: 50%;
background-color: green;
}
//helper functions, it turned out chrome doesn't support Math.sgn()
function signum(x) {
return (x < 0) ? -1 : 1;
}
function absolute(x) {
return (x < 0) ? -x : x;
}
function drawPath(svg, path, startX, startY, endX, endY) {
// get the path's stroke width (if one wanted to be really precize, one could use half the stroke size)
var stroke = parseFloat(path.attr("stroke-width"));
// check if the svg is big enough to draw the path, if not, set heigh/width
if (svg.attr("height") < endY) svg.attr("height", endY);
if (svg.attr("width" ) < (startX + stroke) ) svg.attr("width", (startX + stroke));
if (svg.attr("width" ) < (endX + stroke) ) svg.attr("width", (endX + stroke));
var deltaX = (endX - startX) * 0.15;
var deltaY = (endY - startY) * 0.15;
// for further calculations which ever is the shortest distance
var delta = deltaY < absolute(deltaX) ? deltaY : absolute(deltaX);
// set sweep-flag (counter/clock-wise)
// if start element is closer to the left edge,
// draw the first arc counter-clockwise, and the second one clock-wise
var arc1 = 0; var arc2 = 1;
if (startX > endX) {
arc1 = 1;
arc2 = 0;
}
// draw tha pipe-like path
// 1. move a bit down, 2. arch, 3. move a bit to the right, 4.arch, 5. move down to the end
path.attr("d", "M" + startX + " " + startY +
" V" + (startY + delta) +
" A" + delta + " " + delta + " 0 0 " + arc1 + " " + (startX + delta*signum(deltaX)) + " " + (startY + 2*delta) +
" H" + (endX - delta*signum(deltaX)) +
" A" + delta + " " + delta + " 0 0 " + arc2 + " " + endX + " " + (startY + 3*delta) +
" V" + endY );
}
function connectElements(svg, path, startElem, endElem) {
var svgContainer= $("#svgContainer");
// if first element is lower than the second, swap!
if(startElem.offset().top > endElem.offset().top){
var temp = startElem;
startElem = endElem;
endElem = temp;
}
// get (top, left) corner coordinates of the svg container
var svgTop = svgContainer.offset().top;
var svgLeft = svgContainer.offset().left;
// get (top, left) coordinates for the two elements
var startCoord = startElem.offset();
var endCoord = endElem.offset();
// calculate path's start (x,y) coords
// we want the x coordinate to visually result in the element's mid point
var startX = startCoord.left + 0.5*startElem.outerWidth() - svgLeft; // x = left offset + 0.5*width - svg's left offset
var startY = startCoord.top + startElem.outerHeight() - svgTop; // y = top offset + height - svg's top offset
// calculate path's end (x,y) coords
var endX = endCoord.left + 0.5*endElem.outerWidth() - svgLeft;
var endY = endCoord.top - svgTop;
// call function for drawing the path
drawPath(svg, path, startX, startY, endX, endY);
}
function connectAll() {
// connect all the paths you want!
connectElements($("#svg1"), $("#path1"), $("#teal"), $("#orange"));
connectElements($("#svg1"), $("#path2"), $("#red"), $("#orange"));
connectElements($("#svg1"), $("#path3"), $("#teal"), $("#aqua") );
connectElements($("#svg1"), $("#path4"), $("#red"), $("#aqua") );
connectElements($("#svg1"), $("#path5"), $("#purple"), $("#teal") );
connectElements($("#svg1"), $("#path6"), $("#orange"), $("#green") );
}
$(document).ready(function() {
// reset svg each time
$("#svg1").attr("height", "0");
$("#svg1").attr("width", "0");
connectAll();
});
$(window).resize(function () {
// reset svg each time
$("#svg1").attr("height", "0");
$("#svg1").attr("width", "0");
connectAll();
});
@andreguilhon
Copy link

Woks great almost everytime. I found what I think it may be a tiny bug: When I use a script to reload a page in a few seconds (setTimeout(function(){
window.location.reload(1);
}, 15000);), when the page is reloaded there is no line drawn. If I hit F5 manually, it works great. Do you have any clue of what it can be? Any help would e apreciated!

@joshharington
Copy link

Is there a way to animate this?

@Tommy255
Copy link

Tommy255 commented Sep 1, 2015

Hi,

nice library, Is there a way to make it draw the lines horizontally?

Thank you

@avtarsinghpunia
Copy link

Hi,

I want to draw double side arrows between two div elements?
Can you please suggest something?

Regards,
Avtar Singh

@Azhar157
Copy link

Hello Avtar Singh,

Did you get the required output? Even I want to add arrows in place of a line, could someone help?

Regards,
Azhar

@joeybab3
Copy link

This is cool! The only thing I see that's wrong is that it can't draw the lines horizontally, so it looks weird if you try and put elements side by side, has anyone found a fix for this?

@mdalmazzi
Copy link

Great, I also would like draw the lines horizontally, Any suggestions?

@sridivyaposa
Copy link

If you have two elements side by side, you can use SVG line instead of SVG path. i.e, you can add condition like when startY and endY are equal, you can draw a line instead of drawing a path.

@Manoj-Roy
Copy link

This is awesome plugin. I have use this on my project but here I got a problem. Can I draw line after click on two elements ?

@Manoj-Roy
Copy link

Manoj-Roy commented Mar 7, 2017

How can I create line after click on two div ?

@nghuuphuoc
Copy link

I moved those scripts to this fiddle https://jsfiddle.net/bbcfk164/. So other one can have quick preview and give some modifications if needed :)

@kriit24
Copy link

kriit24 commented Mar 18, 2017

Not working if styleing div elements.
display:inline-block
position:relative
margin
padding

@Kaushik1987
Copy link

is it possible to connects the left/right side-middle point of the any element with the left/right side-middle point of the other element?

for example, I have attached images
case2
Case1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment