I want to build on this question which explains how to draw an arrow between two divs using svg, but I want to not have to position the arrow manually between the divs. Regardless of where the divs are on the page I want to be able to get the right side point of div1, the left side point of div2, and draw an arrow between them.
For example, if the html file was
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="styles/styles.css">
</head>
<body>
<div><p>First div</p></div>
<div><p>Second div</p></div>
</body>
</html>
and the style file was
div{
display: inline-block;
margin-right: 100px;
border-color: green;
border-width: 3px;
border-style: solid;
}
then I would expect something like this:
On the other hand, if the html file was
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="styles/styles.css">
</head>
<body>
<div><p>First div</p></div>
<br><br><br>
<div><p>Second div</p></div>
</body>
</html>
and the style file was
div{
margin-right: 100px;
border-color: green;
border-width: 3px;
border-style: solid;
}
then I would expect to see this:
This is the code that I've worked out so far, based on the comments:
var leftobj = document.getElementById("div1");
var rightobj = document.getElementById("div2");
var leftcoords = leftobj.getBoundingClientRect();
var rightcoords = rightobj.getBoundingClientRect();
var startx = leftcoords.right;
var endx = rightcoords.left;
var starty = (leftcoords.top + leftcoords.bottom) / 2;
var endy = (rightcoords.top + rightcoords.bottom) / 2;
var linepath = `M${startx},${starty} L${endx},${endy}`;
var arr = document.getElementById("arr1");
arr.setAttribute('d', linepath)
<div id="div1"><p>First div</p></div>
<div id="div2"><p>Second div</p></div>
<svg width="300" height="100">
<defs>
<marker id="arrow" markerWidth="13" markerHeight="13" refx="2" refy="6" orient="auto">
<path d="M2,2 L2,11 L10,6 L2,2" style="fill:black;" />
</marker>
</defs>
<path d="M30,150 L100,50" id="arr1"
style="stroke:black; stroke-width: 1.25px; fill: none;marker-end: url(#arrow);" />
</svg>
But this does not draw the arrow where I want it, like I've shown in the pictures. Any help would be super appreciated!
There are several issues that need to be addressed at the same time to achieve what you want.
First of all, you need to be working in the same coordinate system, i.e. the origin (0,0) must be the same for the div
s and the svg
. To achieve this, it is necessary to set the margin
and padding
on all elements to 0 (see the CSS
code).
Next, you need to overlay the svg
on top of the div
s. This can be achieved with CSS
by using the position:fixed
property. And then, you need to wrap your div
s and the svg
in a container div
.
If you want to scroll the page as well, then you need to use the position:absolute
property for the svg
. In addition, you need to get the amount of page scroll and this can be achieved by adding window.scrollX
and window.scrollY
in the relevant places in the JavaScript code.
I increased the size of the svg
to cover more of the page.
I added margin-left: 50px;
to the div
s so that you will be able to see the arrowhead when you experiment with the HTML
to test the second layout you present.
Your JavaScript code seems to be working (however, it is worth avoiding var
today, it is better to use let
or const
).
One problem with this approach is that the line joins up the points that you want to join, but the arrowhead goes further. You would need to draw a shorter line if you wanted the arrowhead to stop on the exact point (or find another way to achieve this).
var leftobj = document.getElementById("div1");
var rightobj = document.getElementById("div2");
var leftcoords = leftobj.getBoundingClientRect();
var rightcoords = rightobj.getBoundingClientRect();
var startx = leftcoords.right + window.scrollX;
var endx = rightcoords.left + window.scrollX;
var starty = (leftcoords.top + leftcoords.bottom) / 2 + window.scrollY;
var endy = (rightcoords.top + rightcoords.bottom) / 2 + window.scrollY;
var linepath = `M${startx},${starty} L${endx},${endy}`;
var arr = document.getElementById("arr1");
arr.setAttribute('d', linepath)
* {
margin: 0;
padding: 0;
}
#div1,
#div2 {
display: inline-block;
padding: 10px;
margin-left: 20px;
margin-right: 100px;
border-color: green;
border-width: 3px;
border-style: solid;
}
svg {
position: absolute;
left: 0px;
top: 0px;
}
.container {
border-width: 0px;
}
<div id="container">
<br>
<br>
<br>
<br>
<br>
<div id="div1">
<p>First div</p>
</div>
<br>
<br>
<div id="div2">
<p>Second div</p>
</div>
<svg width="500" height="500">
<defs>
<marker id="arrow" markerWidth="13" markerHeight="13" refx="2" refy="6" orient="auto">
<path d="M2,2 L2,11 L10,6 L2,2" style="fill:black;" />
</marker>
</defs>
<path d="M30,150 L100,50" id="arr1"
style="stroke:black; stroke-width: 1.25px; fill: none;marker-end: url(#arrow);" />
</svg>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
<br>
</div>