class Container {
constructor(element) {
this.connectors = new Map();
this.element = element;
}
addConnector(topBox, bottomBox, displayHalf = false) {
if (!topBox && !bottomBox) throw new Error("Invalid params");
const connector = new Connector(topBox, bottomBox, displayHalf);
const connectorId = `${topBox.id}:${bottomBox.id}`;
this.element.appendChild(connector.element);
if (this.connectors.has(connectorId)) {
connector.element.style.borderColor = this.connectors.get(connectorId).element.style.borderColor;
} else {
connector.element.style.borderColor = "#" + Math.floor(Math.random() * 16777215).toString(16);
}
this.connectors.set(connectorId, connector);
}
checkConnectors() {
this.connectors.forEach((connector) => {
if (connector.displayHalf) {
connector.firstBox.updateElement();
connector.secondBox.updateElement();
if (connector.firstBox.element && connector.secondBox.element) {
this.addConnector(connector.firstBox, connector.secondBox);
}
}
});
}
}
class Box {
constructor(id) {
this.id = id;
this.updateElement();
}
getMidpoint() {
return this.element.offsetTop + this.element.clientHeight / 2;
}
updateElement() {
this.element ??= document.getElementById(`box${this.id}`);
}
static sortTopDown(firstBox, secondBox) {
return [firstBox, secondBox].sort((a,b) => a.element.offsetTop - b.element.offsetTop);
}
}
class Connector {
constructor(firstBox, secondBox, displayHalf) {
this.firstBox = firstBox;
this.secondBox = secondBox;
this.displayHalf = displayHalf;
const firstBoxHeight = this.firstBox.getMidpoint();
this.element = document.createElement("div");
this.element.classList.add("connector");
this.element.style.top = firstBoxHeight + "px";
let secondBoxHeight;
if (this.displayHalf) {
secondBoxHeight = this.firstBox.element.parentElement.clientHeight;
this.element.style.borderBottom = "unset";
} else {
secondBoxHeight = this.secondBox.getMidpoint();
}
this.element.style.height = Math.abs(secondBoxHeight - firstBoxHeight) + "px";
}
}
const connectButton = document.getElementById("connect");
const error = document.getElementById("error");
const addBoxButton = document.getElementById("addBox");
const container = new Container(document.getElementById("container"));
connectButton.addEventListener("click", () => {
const firstBoxId = document.getElementById("selectFirstBox").value;
const secondBoxId = document.getElementById("selectSecondBox").value;
if (firstBoxId === "" || secondBoxId === "") return;
error.style.display = firstBoxId === secondBoxId ? "block" : "none";
const firstBox = new Box(firstBoxId);
const secondBox = new Box(secondBoxId);
if (!!firstBox.element ^ !!secondBox.element) {
return container.addConnector(firstBox, secondBox, true);
}
const [topBox, bottomBox] = Box.sortTopDown(firstBox, secondBox);
container.addConnector(topBox, bottomBox);
});
window.addEventListener("resize", () => container.checkConnectors());
addBoxButton.addEventListener("click", () => {
const box = document.createElement("div");
box.innerText = 5;
box.id = "box5";
box.classList.add("box");
container.element.appendChild(box);
addBoxButton.style.display = 'none';
container.checkConnectors();
});
.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>
scroll
事件中将会有更多的工作要做,以检查所有连接器是否可见。如果任何一部分可见,则使用addConnector
进行渲染。 - GenericUser