Draw a Connecting Line Between Two Elements

Draw a connecting line between two elements

jsPlumb is an option available that supports drag and drop, as seen by its numerous demos, including the Flowchart demo.

It is available in a free Community edition and a paid Toolkit edition.

The Toolkit edition wraps the Community edition with a comprehensive data binding layer, as well as several UI widgets for building applications and integrations for popular libraries, and is commercially licensed.

CSS Drawing a line between two elements

You need to take border, width and height into account. you cannot draw half a pixel. For example this is a center line:

.A,.B,.C,.D, .E {
position: absolute;
width: 46px;
height: 46px;
-webkit-border-radius: 50%;
-moz-border-radius: 50%;
border-radius: 50%;
border: 2px solid black;
background: lightblue;
}

.Line1{
position: absolute;
left: 50%;
top: 10%;
height: 60%;
width: 2px;
background: black;
transform: translate(24px,23px);
}

How to draw connecting lines between web elements on a page

You might be able to apply something like this by taking a few measurements from the boxes you want to connect; offsetTop and clientHeight.


Update Added some logic for undrawn cards requirement.

While this doesn't fully simulate dynamic populating of cards, I made an update to show how to handle a scenario where only one card is drawn.

  1. Click connect using the default values (1 and 5). This will show an open connector starting from box 1.
  2. Click "Add box 5". This will add the missing box and update the connector.

The remaining work here is to create an event listener on scroll to check the list of connectors. From there you can check if both boxes appear or not in the DOM (see checkConnectors function). If they appear, then pass values to addConnector which will connect them fully.

const connectButton = document.getElementById("connect");
const container = document.getElementById("container");
const error = document.getElementById("error");
const addBoxButton = document.getElementById("addBox");

const connectors = new Map();

const getMidpoint = element => element.offsetTop + element.clientHeight / 2;
const getElementAtBoxId = id => document.getElementById(`box${id}`);

connectButton.addEventListener("click", () => {
const firstBoxId = document.getElementById("selectFirstBox").value;
const secondBoxId = document.getElementById("selectSecondBox").value;

if (firstBoxId === null && secondBoxId === null) return;

error.style.display = firstBoxId === secondBoxId ? "block" : "none";

if (firstBoxId === secondBoxId) return;

const firstInput = getElementAtBoxId(firstBoxId);
const secondInput = getElementAtBoxId(secondBoxId);

// Check for undrawn cards
if (firstInput && !secondInput || !firstInput && secondInput) {
addConnector(firstBoxId, firstInput, secondBoxId, secondInput, true);
return;
}

const firstBox = firstInput.offsetTop < secondInput.offsetTop ? firstInput : secondInput;
const secondBox = firstInput.offsetTop < secondInput.offsetTop ? secondInput : firstInput;

addConnector(firstBoxId, firstBox, secondBoxId, secondBox);
});

function addConnector(firstBoxId, firstBox, secondBoxId, secondBox, half = false) {
const args = { firstBoxId, firstBox, secondBoxId, secondBox, half };
if (!firstBox && !secondBox) throw new Error(`Invalid params: ${JSON.stringify(args)}`);

const connectorId = `${firstBoxId}:${secondBoxId}`;
let connector, color;

if (connectors.has(connectorId)) color = connectors.get(connectorId).color;
else color = '#' + Math.floor(Math.random() * 16777215).toString(16);

if (half) {
const box = firstBox ? firstBox : secondBox;
// if firstBox draw up else draw down
if (firstBox) {
connector = buildConnector(getMidpoint(box), box.parentElement.clientHeight);
connector.style.borderBottom = "unset";
} else {
// similar logic for draw up
}
} else {
connector = buildConnector(getMidpoint(firstBox), getMidpoint(secondBox));
}

connector.style.borderColor = color;

container.appendChild(connector);
connectors.set(connectorId, { ...args, color });
}

function buildConnector(height1, height2) {
const connector = document.createElement("div");

connector.classList.add("connector");
connector.style.top = `${height1}px`;
connector.style.height = `${Math.abs(height2 - height1)}px`;

return connector;
}

window.addEventListener("resize", () => {
for (const connector of connectors.values()) {
const { firstBoxId, firstBox, secondBoxId, secondBox, half } = connector;
if(firstBox || secondBox) {
addConnector(firstBoxId, firstBox, secondBoxId, secondBox, half);
console.log(`resize detected, adjusting connector between ${firstBoxId} and ${secondBoxId}`);
}
}
});

addBoxButton.addEventListener("click", () => {
const box = document.createElement("div");
box.innerText = 5;
box.id = "box5";
box.classList.add("box");
container.appendChild(box);

addBoxButton.style.display = 'none';

// check if connectors need to be updated
checkConnectors();
});

function checkConnectors() {
for (const connector of connectors.values()) {
if (connector.half) {
let { firstBox, firstBoxId, secondBox, secondBoxId } = connector;

firstBox = firstBox ? firstBox : getElementAtBoxId(firstBoxId);
secondBox = secondBox ? secondBox : getElementAtBoxId(secondBoxId);

// both boxes in DOM -> update connector
if (firstBox && secondBox) {
addConnector(firstBoxId, firstBox, secondBoxId, secondBox);
}
}
}
}
.box {
border: solid 1px;
width: 60px;
margin-left: 30px;
margin-bottom: 5px;
text-align: center;
}

#inputs {
margin-top: 20px;
}

#inputs input {
width: 150px;
}

.connector {
position: absolute;
border-top: solid 1px;
border-left: solid 1px;
border-bottom: solid 1px;
width: 29px;
}

#error {
display: none;
color: red;
}
<div id="container">
<div id="box1" class="box">1</div>
<div id="box2" class="box">2</div>
<div id="box3" class="box">3</div>
<div id="box4" class="box">4</div>
</div>
<div id="inputs">
<input id="selectFirstBox" type="number" placeholder="Provide first box id" min="1" value="1" max="5" />
<input id="selectSecondBox" type="number" placeholder="Provide second box id" min="1" value="5" max="5" />
<div id="error">Please select different boxes to connect.</div>
</div>
<button id="connect">Connect</button>
<button id="addBox">Add box 5</button>

Draw line from one text element to another with css

Here's a possible CSS only solution. I removed all the table display settings and used flex on the p elements which contain the spans. Also, I used relative positions on some of the elements in order to be able to move them vertically in order to achieve vertically centered alignment between text and lines:

#div1 {
width: 400px;
padding: 0 20px;
}

#div1-1 {
border-right: dashed 1px #000000;
padding-bottom: 10px;
}

#div1-2 {
margin-left: 100px;
margin-top: -15px;
}

#div1-1 p,
#div1-2 p {
display: flex;
position: relative;
top: -0.6em;
}

#div1-1 p span,
#div1-2 p span{
padding-right: 10px;
}

span.horizontal_dotted_line {
border-bottom: 1px dashed #000000;
flex-grow: 1;
position: relative;
top: -0.5em;
padding-left: 3px;
}
<div id="div1">

<div id="div1-1">
<p><span>Cell 1-1-1 </span> <span class="horizontal_dotted_line"></span></p>
<p><span>Cell 1-1-2</span></p>
<p><span>Cell 1-1-3</span></p>
<p><span>Cell 1-1-4</span></p>
</div>

<div id="div1-2">
<p><span>Cell 1-2-1 </span> <span class="horizontal_dotted_line"></span></p>
<p><span>Cell 1-2-2</span></p>
<p><span>Cell 1-2-3</span></p>
<p><span>Cell 1-2-4</span></p>
</div>

</div>

Draw line between 2 elements in reactJS

You can use the <hr element in HTML. This draws a line between the two elements.

You can see more information about it on

https://developer.mozilla.org/en-US/docs/Web/HTML/Element/hr

Draw a line from one element to multiple elements on click

I fixed your fiddle: https://jsfiddle.net/c4ju6a0p/

Code changes:

// Get actual position relative to viewport.
// See https://stackoverflow.com/a/11396681/117030
fromBCR = from.getBoundingClientRect();
toBCR = to.getBoundingClientRect();

var fT = fromBCR.top + from.offsetHeight / 2;
var tT = toBCR.top + to.offsetHeight / 2;

// Don't add offsetWidth. This connects to the middle, not the left edge.
var fL = fromBCR.left //+ from.offsetWidth / 2;
var tL = toBCR.left + to.offsetWidth / 2;


  • The problem was the line was being calculated with the incorrect position due to relative positioning. This can be seen more clearly when the relative CSS is commented out: https://jsfiddle.net/vust5nxf/
  • Also, don't add the offsetWidth if you want the line to go to the left edge.

update: didn't notice the code snippet... applied changes there, too. I also made one more change:

  • You need to pass the element that was clicked to adjustLine(), otherwise currently the line is drawn between the same two elements every time because the elements are hardcoded with ids.
  • As a style note: I would move the definition of function adjustLine() outside the click handler. This will make the code easier to read, and the function will only be created once, instead of every time a click is handled.
adjustLine(
this, // Element that was clicked.
document.getElementById('div2'),
document.getElementById('line')
);

$('.seriesli').click(function() {

function adjustLine(from, to, line) {

// Get actual position relative to viewport.
// See https://stackoverflow.com/a/11396681/117030
fromBCR = from.getBoundingClientRect();
toBCR = to.getBoundingClientRect();

var fT = fromBCR.top + from.offsetHeight / 2;
var tT = toBCR.top + to.offsetHeight / 2;

// Don't add offsetWidth. This connects to the middle, not the left edge.
var fL = fromBCR.left //+ from.offsetWidth / 2;
var tL = toBCR.left + to.offsetWidth / 2;

var CA = Math.abs(tT - fT);
var CO = Math.abs(tL - fL);
var H = Math.sqrt(CA * CA + CO * CO);
var ANG = 180 / Math.PI * Math.acos(CA / H);

if (tT > fT) {
var top = (tT - fT) / 2 + fT;
} else {
var top = (fT - tT) / 2 + tT;
}
if (tL > fL) {
var left = (tL - fL) / 2 + fL;
} else {
var left = (fL - tL) / 2 + tL;
}

if ((fT < tT && fL < tL) || (tT < fT && tL < fL) || (fT > tT && fL > tL) || (tT > fT && tL > fL)) {
ANG *= -1;
}
top -= H / 2;

line.style["-webkit-transform"] = 'rotate(' + ANG + 'deg)';
line.style["-moz-transform"] = 'rotate(' + ANG + 'deg)';
line.style["-ms-transform"] = 'rotate(' + ANG + 'deg)';
line.style["-o-transform"] = 'rotate(' + ANG + 'deg)';
line.style["-transform"] = 'rotate(' + ANG + 'deg)';
line.style.top = top + 'px';
line.style.left = left + 'px';
line.style.height = H + 'px';
}
adjustLine(
this, // Element that was clicked.
document.getElementById('div2'),
document.getElementById('line')
);
});
.identifier {
width: 10px;
height: 10px;
background-color: red;
position: absolute;
right: 45%;
top: 50%;
}

.series-div {
position: absolute;
right: 5%;
bottom: 30%;
}

.series-ul li {
list-style: none;
color: grey;
font-size: 1em;
font-weight: 600;
border: 2px solid grey;
display: table;
padding: 0.3em 0.1em;
text-align: center;
margin: 0.5em;
cursor: pointer;
}

#line {
position: absolute;
width: 2px;
margin-top: -1px;
background-color: red;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div style="position;relative;">
<div class="identifier" id="div2"></div>
<div class="series-div">
<ul class="series-ul">
<li class="seriesli" id="div1">A series</li>

<li class="seriesli">B series</li>

<li class="seriesli">C series</li>

</ul>
</div>
<div id="line"></div>
<img src="https://stat.overdrive.in/wp-content/odgallery/2020/06/57263_2020_Mercedes_Benz_GLS.jpg" class="img-responsive firstcar-detail" style="width: 100%;">

</div>


Related Topics



Leave a reply



Submit