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.
- Click connect using the default values (1 and 5). This will show an open connector starting from box 1.
- 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 therelative
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
Regex for Names Validation Allow Only Letters and Spaces
Open Html5 Date Picker on Icon Click
Ngx-Datatable Set Column Width Dynamically
How to Extract the User Name from an Email Address Using JavaScript
Select Li Element With Arrow Keys (Up and Down) Using JavaScript (Jquery)
How to Pass Id from Modal in Boostrap to PHP File
How to Use If Statement Inside Json
How to Force Clients to Refresh JavaScript Files
Draw a Connecting Line Between Two Elements
How to Hide the Parent Div If All Its Child Div Are Hidden
How to Add a Dynamic Action That Will Redirect to Another Page by Executing JavaScript Code
How to Change Size of Mat-Icon on Angular Material
How to Check a Radio Button in the Table Row Based on That Column Header
How to Enable Scrolling on Website That Disabled Scrolling
Is It Any Limit for Post Data Size in Ajax
Checking for Null Is Not Working for Ajax Json Data