Engineering Journal
Schema Editor
Schema Editor

Topology Analyzer Reports Phantom Connections After Pressing Escape to Cancel a Wire

2026-06-04

TLDR

Pressing Escape during a wire draw operation removes the active wire element visually from the SVG DOM, but can leave a stale wire object inside the internal wire registry array. When a subsequent topology analysis runs, calling getBBox() on the detached DOM element returns a zero-area bounding box at coordinate $(0, 0)$. The analyzer interprets this zero bbox as a real wire endpoint landing at the origin, generating ghost net connections. Sanitizing the registry with element.isConnected before analysis passes eliminates phantom nodes.
Topology Pass StateDOM Element StatusgetBBox() OutputNetlist Result
Un-sanitized RegistryDetached node (isConnected === false){ x: 0, y: 0, w: 0, h: 0 }Ghost connection at origin (BUG)
Purged RegistryAttached node (isConnected === true)Exact world coordinates100% Accurate Netlist

Problem statement: the invisible ghost connection bug

A user starts drawing a wire, changes their mind, and hits Escape. Visually, the canvas cleans up cleanly.

However, when running the next netlist validation pass or BOM export, the editor reports an electrical connection between two components placed near the canvas origin, components that were never connected by the user.


Technical failure mode: asynchronous store desynchronization

The root cause is a partial deletion bug between the SVG DOM tree and the internal JavaScript wire registry:

// DEFECTIVE IMPLEMENTATION: Partial wire cleanup
function cancelWire() {
  activeWire?.element?.remove(); // Removes SVG node from DOM...
  activeWire = null;
  // DEFECT: wiresArray still contains the cancelled wire object!
}

When analyzeTopology() executes, it iterates wiresArray and calls element.getBBox(). Calling getBBox() on an un-attached SVG element returns { x: 0, y: 0, width: 0, height: 0 }. The topology analyzer registers this as a wire located at $(0, 0)$, creating ghost net connections with any component situated at the origin.


The fix & architecture: self-healing DOM parity filters

Implement a unified cleanupWire() helper that updates both the DOM and internal registry, while adding a self-healing element.isConnected guard before analysis passes:

// 1. Unified Cleanup Helper
function cleanupWire(wireObj) {
  if (!wireObj) return;

// Remove DOM node wireObj.element?.remove();

// Remove from registry array wiresArray = wiresArray.filter(w => w !== wireObj && w.element?.isConnected);

if (activeWire === wireObj) activeWire = null; }

// 2. Defensive Analysis Pre-Filter function analyzeTopology() { // Purge any stale wire objects whose elements are no longer connected to the DOM wiresArray = wiresArray.filter(w => w.element?.isConnected);

// ... Proceed with netlist graph construction }

Rule of thumb: Never remove a DOM node without updating its corresponding memory registry. Guard DOM-backed registry iterations using element.isConnected to filter out detached nodes automatically.
Read this post in the full Engineering Journal →