controls

controls

Define which areas can initiate dragging

The controls plugin defines which parts of an element can be used to start dragging. Think modal headers, card handles, or toolbar grips - only specific areas should be draggable while the rest remains interactive.

controls({ allow: ControlFrom.selector('.handle') }); // Only handle can drag
controls({ block: ControlFrom.selector('.button') }); // Buttons can't drag
controls({
  allow: ControlFrom.selector('.header'),
  block: ControlFrom.selector('.close'),
}); // Header drags, close button doesn't

Basic Usage

import { controls, ControlFrom, Draggable } from '@neodrag/vanilla';

const allowElement = document.getElementById('allow-only');
const blockElement = document.getElementById('block-some');

new Draggable(allowElement, [
  controls({ allow: ControlFrom.selector('.header') }),
]);

new Draggable(blockElement, [
  controls({ block: ControlFrom.selector('button') }),
]);

ControlFrom Utilities

Selector-Based Controls

import { controls, ControlFrom, Draggable } from '@neodrag/vanilla';

const single = document.getElementById('single-allow');
const multi = document.getElementById('multi-allow');
const block = document.getElementById('block-some');

new Draggable(single, [
  controls({ allow: ControlFrom.selector('.handle') }),
]);

new Draggable(multi, [
  controls({ allow: ControlFrom.selector('.handle, .title, .grip') }),
]);

new Draggable(block, [
  controls({ block: ControlFrom.selector('button, input, select') }),
]);

Element-Based Controls

import { controls, ControlFrom, Draggable } from '@neodrag/vanilla';

const container = document.getElementById('container');
const handle = document.getElementById('handle');
const blockContainer = document.getElementById('block-container');
const blockElement = document.getElementById('block-element');

new Draggable(container, [
  controls({ allow: () => ControlFrom.elements([handle]) }),
]);

new Draggable(blockContainer, [
  controls({ block: () => ControlFrom.elements([blockElement]) }),
]);

Advanced Control Logic

Priority Handling

import { controls, ControlFrom, Draggable } from '@neodrag/vanilla';

const allowWins = document.getElementById('allow-wins');
const blockWins = document.getElementById('block-wins');

new Draggable(allowWins, [
  controls({
    allow: ControlFrom.selector('.drag-zone'),
    block: ControlFrom.selector('.no-drag'),
    priority: 'allow',
  }),
]);

new Draggable(blockWins, [
  controls({
    allow: ControlFrom.selector('.drag-zone'),
    block: ControlFrom.selector('.no-drag'),
    priority: 'block',
  }),
]);

Real-World Examples

import {
  Draggable,
  controls,
  ControlFrom,
  bounds,
  BoundsFrom,
} from '@neodrag/vanilla';

const modal = document.getElementById('modal');

new Draggable(modal, [
  controls({
    allow: ControlFrom.selector('.modal-header'),
    block: ControlFrom.selector('.modal-close, .modal-body button'),
  }),
  bounds(BoundsFrom.viewport()),
]);

How It Works

The controls plugin manages drag initiation permissions:

  1. Finds control zones on setup by querying the DOM
  2. Checks click position in shouldStart hook
  3. Determines permission based on which zones contain the click point
  4. Returns boolean to allow/prevent drag start

Zone priority logic:

  • If allow zones exist and click is outside all of them → block
  • If click is in both allow and block zones → use priority option
  • Smaller (nested) zones take precedence over larger ones

API Reference

function controls(options?: {
  allow?: ReturnType<
    typeof ControlFrom.selector | typeof ControlFrom.elements
  >;
  block?: ReturnType<
    typeof ControlFrom.selector | typeof ControlFrom.elements
  >;
  priority?: 'allow' | 'block';
}): Plugin;

Options:

  • allow - Control zones that can initiate dragging
  • block - Control zones that cannot initiate dragging
  • priority - Which wins when zones overlap (‘allow’ | ‘block’)

ControlFrom utilities:

  • ControlFrom.selector(cssSelector) - Elements matching CSS selector
  • ControlFrom.elements(nodeList) - Specific DOM elements

Returns: A plugin object for use with draggable.