Skip to content

How to fix: How can I make the docs js library draw a border when I move a table?

In the context of document manipulation libraries (such as those managing DOM-based editors or Canvas-based engines), the “missing border” issue occurs during the translation phase of a table object.

When you initiate a move operation, the library typically detaches the element from the standard flow or high-frequency re-renders the object’s position. If the library’s internal onMove or onDrag hooks are not explicitly configured to render a “ghosting” layer or update the stroke properties of the bounding box, the table appears to float without visual boundaries, leading to a poor user experience (UX) and precision issues.

Cause Technical Explanation
Render Loop Exclusion The border rendering logic is often omitted from the high-frequency move loop to save CPU cycles.
CSS Pointer Events The border/overlay might be intercepting mouse events, causing the library to lose the “drag” focus, leading developers to disable it.
Z-Index Layering The moving table is lifted to a top-level “drag layer” that lacks the inherited styles of the original container.
Canvas State Reset In Canvas-based libraries (like Fabric.js), the stroke property may be set to transparent during transformation by default.

Solution 1: Implementing a Proxy Ghost Border (DOM-based)

Section titled “Solution 1: Implementing a Proxy Ghost Border (DOM-based)”

If your library uses standard DOM elements, you should create a secondary “proxy” element that follows the cursor. This prevents expensive layout reflows of the actual table.

// Attach to your library's 'dragstart' event
const handleTableDrag = (tableEl, event) => {
const ghost = tableEl.cloneNode(false); // Shallow clone
ghost.style.position = 'absolute';
ghost.style.border = '2px dashed #3b82f6';
ghost.style.pointerEvents = 'none'; // Critical: allows mouse events to pass through
ghost.style.zIndex = '9999';
ghost.id = 'table-move-proxy';
document.body.appendChild(ghost);
const moveHandler = (e) => {
ghost.style.left = `${e.pageX}px`;
ghost.style.top = `${e.pageY}px`;
};
window.addEventListener('mousemove', moveHandler);
window.addEventListener('mouseup', () => {
window.removeEventListener('mousemove', moveHandler);
ghost.remove();
}, { once: true });
};

Solution 2: Forcing Stroke Rendering (Canvas/Fabric.js)

Section titled “Solution 2: Forcing Stroke Rendering (Canvas/Fabric.js)”

If using a library like Fabric.js, the “border” is controlled by hasBorders and borderDashArray. You must ensure the object is set to “active” during the move.

// Example for Canvas-based document engines
tableObject.set({
hasBorders: true,
borderScaleFactor: 2,
borderColor: 'rgba(0, 0, 255, 0.5)',
borderDashArray: [5, 5]
});
// Force the engine to render the control layer during movement
canvas.on('object:moving', function(e) {
const obj = e.target;
obj.setCoords(); // Recalculate boundary coordinates
canvas.requestRenderAll(); // Trigger the render pipeline
});

Solution 3: CSS-Only Highlight via Attribute Toggle

Section titled “Solution 3: CSS-Only Highlight via Attribute Toggle”

Most modern JS editors toggle a data-moving attribute. You can use hardware-accelerated CSS to render the border.

/* CSS */
.table-node[data-is-moving="true"] {
outline: 2px solid #4CAF50 !important;
outline-offset: 4px;
transition: outline 0.1s ease-in-out;
box-shadow: 0 0 10px rgba(0,0,0,0.2);
}
// JS Logic
table.on('dragstart', () => table.setAttribute('data-is-moving', 'true'));
table.on('dragend', () => table.removeAttribute('data-is-moving'));
  1. Use requestAnimationFrame: When drawing custom borders on a canvas, always wrap your draw calls in requestAnimationFrame to prevent stuttering.
  2. Pointer Events: Ensure any overlay or border element has pointer-events: none set. If you forget this, the mouse cursor will “hit” the border, trigger a mouseleave on the table, and break the drag operation.
  3. BBox Calculation: If the table is large, calculate the Bounding Box (BBox) once at mousedown and move a simple div of the same dimensions rather than re-rendering the whole table content.
  4. Hardware Acceleration: Use transform: translate3d(x, y, 0) for moving the border instead of top/left to ensure the border moves at a consistent 60fps.