When I build drag-and-drop UIs, the first problem I hit is not the drag itself—it is the constant change in requirements. Today you want a simple list; tomorrow you need placeholders, delayed dragging, axis locks, or extra tolerance rules. Rewriting the widget every time is a waste. The option() method for jQuery UI Sortable is the control panel I rely on when I need to read, change, or bulk-update behavior without tearing down the widget. If you already use jQuery UI Sortable, this method is the hinge between a static setup and a living, adjustable UI.
I will show how to read a single option, read all options, set one option, and set many options at once. I will also walk through practical patterns I use in production: runtime feature toggles, accessibility tuning, and safe resets after AJAX updates. You will see complete, runnable examples with real-world names instead of toy placeholders. I will call out common mistakes, edge cases, and performance considerations, and I will explain when I purposely avoid option() and choose another approach.
A short mental model of Sortable options
The Sortable widget is a small state machine with a set of configuration flags. Each flag is an option: axis, delay, distance, placeholder, handle, items, disabled, and many more. When you call $(selector).sortable({ ... }), you are setting those flags at creation time. But the UI rarely stays frozen. Teams add settings, support questions, or a flag that changes after a user action. That is where option() comes in.
I think of option() as the getter and setter for this state machine. It can:
- return a single value you ask for
- return all values as a map
- set one value
- set many values in one call
The method works on the same widget instance, so you do not need to destroy and recreate the widget. That matters for performance and for preserving any live references or event handlers.
A useful analogy I keep in mind is “feature flags on a moving machine.” The widget is the machine, options are the flags, and option() is the switchboard. The machine keeps running while you flip switches, which means you can evolve behavior in real time as long as you respect the boundaries of that machine (for example, don’t change the frame while it is moving).
Syntax patterns you actually use
I use four patterns and I keep them in muscle memory. The syntax is short, but the details matter, especially the types you pass.
Get a single option
If you want one value, pass the option name as a string. The return value type matches the option type.
const isDisabled = $(‘#taskList‘).sortable(‘option‘, ‘disabled‘);
That returns a boolean. If the option is a number like delay, you will get a number. If the option is a function like start, you get the function reference.
Get all options
If you want the entire configuration at runtime, call with no parameters. You will get a plain object with all options.
const allOptions = $(‘#taskList‘).sortable(‘option‘);
I use this for diagnostics or for saving settings. It is also useful for asserting that a configuration load applied correctly.
Set one option
Pass the option name and the new value. The widget updates immediately.
$(‘#taskList‘).sortable(‘option‘, ‘axis‘, ‘y‘);
Set multiple options
Pass a map of options. The widget applies them as a batch.
$(‘#taskList‘).sortable(‘option‘, {
delay: 150,
distance: 6,
disabled: false
});
I prefer this for any update that changes more than one behavior; it keeps intent clear and reduces small timing issues in the browser.
A full, runnable example with real UI controls
Below is a complete HTML file you can paste into a file and open. It uses modern attribute names, real list content, and a panel of controls that read and set options at runtime. I keep the structure plain to show the method clearly.
Sortable option() demo
body { font-family: system-ui, -apple-system, Arial, sans-serif; margin: 24px; }
#taskList { list-style: none; padding: 0; width: 360px; }
#taskList li { margin: 8px 0; padding: 10px 12px; background: #e9f8ef; border: 1px solid #b8e3c4; cursor: move; }
.controls { margin-top: 16px; display: grid; gap: 8px; width: 360px; }
.row { display: flex; gap: 8px; align-items: center; }
button { padding: 6px 10px; }
.readout { font-size: 14px; color: #2b3b33; }
Sortable option() runtime control
- Design review – Q1 dashboard
- QA pass – billing flow
- Content update – onboarding email
- Release note draft – version 4.2
$(function () {
$(‘#taskList‘).sortable({
axis: ‘y‘,
delay: 0,
distance: 4
});
function updateReadouts() {
const axis = $(‘#taskList‘).sortable(‘option‘, ‘axis‘);
const disabled = $(‘#taskList‘).sortable(‘option‘, ‘disabled‘);
$(‘#axisReadout‘).text(axis: ${axis || ‘free‘});
$(‘#disabledReadout‘).text(disabled: ${disabled});
}
$(‘#toggleAxis‘).on(‘click‘, function () {
const currentAxis = $(‘#taskList‘).sortable(‘option‘, ‘axis‘);
const nextAxis = currentAxis === ‘y‘ ? false : ‘y‘;
$(‘#taskList‘).sortable(‘option‘, ‘axis‘, nextAxis);
updateReadouts();
});
$(‘#toggleDisabled‘).on(‘click‘, function () {
const current = $(‘#taskList‘).sortable(‘option‘, ‘disabled‘);
$(‘#taskList‘).sortable(‘option‘, ‘disabled‘, !current);
updateReadouts();
});
$(‘#showOptions‘).on(‘click‘, function () {
const options = $(‘#taskList‘).sortable(‘option‘);
const count = Object.keys(options).length;
$(‘#optionCount‘).text(options: ${count});
});
updateReadouts();
});
This example does three things: it toggles axis, toggles disabled, and shows the total number of options in the widget. Each action uses option() in a different way. It also shows a small but important detail: I read current values before I set new ones, which keeps the UI consistent and avoids a “guess the state” bug.
Reading options safely in live UIs
I use option() getters most often in two cases: diagnostics and conditional UI. Diagnostics are straightforward, but conditional UI has nuance. Here is a pattern I trust.
function canReorderTasks() {
return !$(‘#taskList‘).sortable(‘option‘, ‘disabled‘);
}
function requireVerticalOnly() {
const axis = $(‘#taskList‘).sortable(‘option‘, ‘axis‘);
return axis === ‘y‘;
}
This looks simple, but it keeps view logic consistent with widget state. If you hardcode flags in your app state and forget to update the widget, you end up with a false sense of control. Reading directly from the widget avoids that mismatch.
I also check for an option that might not be set. If a value is false or null, I handle it explicitly. In the example above I handle a false axis by displaying “free” in the UI. That tiny piece keeps the UI honest, which is the difference between trust and confusion in a drag-and-drop interface.
Setting one option without breaking UX
Setting a single option is attractive, but it can disrupt user experience if you change behavior mid-drag or mid-animation. I use a few rules:
- I avoid changing
axis,containment, orhelperwhile a drag is happening. - I update settings in response to explicit user actions, not background events.
- I follow the change with a UI hint so the user understands the new rule.
Here is a pattern I use when the UI has a “precision mode.” It sets distance to reduce accidental drags when people are trying to select text.
$(‘#precisionModeToggle‘).on(‘change‘, function () {
const precisionOn = this.checked;
$(‘#taskList‘).sortable(‘option‘, ‘distance‘, precisionOn ? 12 : 4);
$(‘#precisionStatus‘).text(
precisionOn ? ‘Precision mode: on‘ : ‘Precision mode: off‘
);
});
That change is immediate and it feels good because it matches the user action. I avoid automatically toggling this based on device type or other heuristics unless I also show a UI prompt.
Setting multiple options as a coordinated update
When I need to update multiple settings together, I always use the multi-option pattern. It makes a semantic guarantee: “apply this policy as a unit.” The widget does not really batch updates, but the intent is clear, and I can easily log or roll back the changes.
Imagine a “low-power” mode for a tablet that sacrifices animation for smooth scrolling. I might change distance, delay, and scrollSensitivity at once.
function applyLowPowerMode() {
$(‘#taskList‘).sortable(‘option‘, {
delay: 200,
distance: 10,
scrollSensitivity: 40
});
}
function applyStandardMode() {
$(‘#taskList‘).sortable(‘option‘, {
delay: 0,
distance: 4,
scrollSensitivity: 20
});
}
The result is predictable because all three options move together. If I set them one by one, I have more chances to leave the widget in a weird state if an error occurs or a third-party script throws in the middle.
Option categories and what tends to change at runtime
Not every option is a good candidate for runtime updates. I group options into categories so I know which are safe to tweak and which deserve caution.
1) Interaction feel
These are the knobs that change how the drag feels to a user.
distanceanddelaymake the drag feel deliberate vs eager.toleranceaffects what counts as a valid drop intersection.scrollSpeedandscrollSensitivityinfluence auto-scroll.
These are ideal for option() because they are rarely tied to DOM structure. I often expose them as user preferences for power users or accessibility needs.
2) Constraints and geometry
These affect where the item can move.
axislimits toxory.containmentlimits movement to a container or parent.gridsnaps movement to a grid.
I do change these at runtime, but I never change them during a drag. If I need to switch geometry rules dynamically, I disable dragging briefly, set the options, then re-enable to avoid a jarring transition.
3) Structure and targeting
These options are tied to DOM structure.
itemscontrols which descendants are draggable.handlecontrols which sub-element starts the drag.connectWithlinks lists.
I treat these as “structural options.” If I change them often, I usually re-init or call refresh() afterward. option() can set them, but the risk of stale selectors is higher.
4) Event hooks
start, update, receive, stop, and similar options are functions. I do set these at runtime, but I use them in a deliberate way, usually to swap logging or analytics hooks without changing the rest of the widget.
Runtime feature toggles with real UI toggles
A pattern I use all the time is a little panel of toggles that drives multiple options at once. This is especially good for internal tools where support or operations teams need to tweak behavior without touching code.
const defaultPolicy = {
axis: ‘y‘,
delay: 0,
distance: 4,
tolerance: ‘intersect‘
};
const safeModePolicy = {
axis: ‘y‘,
delay: 150,
distance: 10,
tolerance: ‘pointer‘
};
function applyPolicy(policy) {
$(‘#taskList‘).sortable(‘option‘, policy);
}
$(‘#modeSelect‘).on(‘change‘, function () {
const value = this.value;
if (value === ‘safe‘) applyPolicy(safeModePolicy);
if (value === ‘default‘) applyPolicy(defaultPolicy);
});
This keeps the UI simple and makes the tradeoffs explicit. “Safe mode” typically reduces accidental drags and tightens the drop rules, while “default” restores speed. I like to show a brief hint when switching so users understand why the list feels different.
Accessibility and keyboard considerations
Sortable is primarily a mouse and touch interaction, so I treat accessibility as a first-class requirement. option() helps because I can tune drag sensitivity and handle behavior without re-init, but I also use it to align with keyboard and focus patterns.
Here is my checklist and the practical ways I use options to support it:
- I provide a visible handle and set
handleso only the handle is draggable. That reduces accidental drags for keyboard and screen reader users. - I increase
distanceslightly to allow text selection and to avoid accidental drags when someone is trying to focus or click. - I use
cancelto prevent dragging on form fields inside items. - I apply ARIA labels and
aria-grabbedstate manually and update them instartandstopcallbacks.
A light, pragmatic example:
$(‘#taskList‘).sortable({
handle: ‘.drag-handle‘,
distance: 6,
cancel: ‘input, textarea, button, select‘
});
$(‘#taskList‘).sortable(‘option‘, ‘start‘, function (event, ui) {
ui.item.attr(‘aria-grabbed‘, ‘true‘);
});
$(‘#taskList‘).sortable(‘option‘, ‘stop‘, function (event, ui) {
ui.item.attr(‘aria-grabbed‘, ‘false‘);
});
Notice how I use option() to change the callback without re-initializing the widget. If I need to integrate with an accessibility tool or analytics library, I can swap the handler live.
If your app already supports keyboard reordering through dedicated buttons, keep that and use sortable(‘option‘, ‘disabled‘, true) when keyboard mode is active. This avoids conflicting drag behaviors and gives assistive tech a consistent path.
Working with connected lists and changing groups
Connecting lists is a common use case: backlog, in-progress, done. The connectWith option allows items to move between lists, but it adds complexity when you want to change connections on the fly.
I use option() to update connectWith only when the list structure is stable. Then I call refresh() on every list involved. This sequence avoids half-connected states:
function updateConnections(selector, connectWith) {
$(selector).each(function () {
$(this).sortable(‘option‘, ‘connectWith‘, connectWith);
});
$(selector).each(function () {
$(this).sortable(‘refresh‘);
});
}
updateConnections(‘.board-column‘, ‘.board-column‘);
If a list is added or removed dynamically, I either re-init the new list with the same options or run refresh() on all lists after insertion. I do not rely on option() alone to pick up new items or containers because Sortable caches some details at init time.
Placeholder and helper tuning at runtime
The placeholder is the “ghost slot” where the dragged item will land. In production, a readable placeholder can cut user errors significantly, especially in dense lists. option() lets me switch placeholder classes or helper types based on context.
A simple placeholder toggle:
$(‘#taskList‘).sortable({
placeholder: ‘task-placeholder‘
});
$(‘#compactMode‘).on(‘change‘, function () {
const cls = this.checked ? ‘task-placeholder-compact‘ : ‘task-placeholder‘;
$(‘#taskList‘).sortable(‘option‘, ‘placeholder‘, cls);
});
Helpers are also powerful. helper: ‘clone‘ is useful for preserving layout. A function helper can add badges or metadata to the drag proxy. I often test the helper separately, then set it via option() once it is stable.
function buildHelper(e, item) {
const $helper = item.clone();
$helper.addClass(‘drag-proxy‘);
$helper.find(‘.meta‘).remove();
return $helper;
}
$(‘#taskList‘).sortable(‘option‘, ‘helper‘, buildHelper);
When I swap helper types at runtime, I also ensure the CSS for the helper is already loaded to avoid layout flashes.
Event options and live instrumentation
One of the most underestimated uses of option() is swapping event handlers at runtime. This is great for analytics, instrumentation, or temporary debugging.
For example, when I want to log reorder events only in a diagnostic session:
function logUpdate(event, ui) {
const id = ui.item.data(‘id‘);
console.log(‘Reordered item‘, id);
}
$(‘#taskList‘).sortable(‘option‘, ‘update‘, logUpdate);
Later, I can remove the handler by setting it to null or a no-op.
$(‘#taskList‘).sortable(‘option‘, ‘update‘, null);
This is cleaner than binding and unbinding DOM events manually because you keep your widget behavior centralized in one place.
Safe resets after AJAX updates
Dynamic data updates are a reality. The list changes, and you need to keep Sortable in sync. option() does not refresh the DOM on its own, so I use a simple, predictable sequence:
- Disable sorting to prevent user actions during updates.
- Update DOM with new list items.
- Call
refresh()to sync Sortable. - Re-enable sorting and re-apply any runtime options.
async function refreshListWithData(items) {
$(‘#taskList‘).sortable(‘option‘, ‘disabled‘, true);
const $list = $(‘#taskList‘);
$list.empty();
items.forEach(item => {
$list.append(${item.label} );
});
$list.sortable(‘refresh‘);
$list.sortable(‘option‘, {
disabled: false,
distance: 6
});
}
I make refresh() a habit whenever the DOM structure changes. It keeps the internal state aligned and prevents “ghost drag” bugs where the widget thinks items exist but they don’t.
Common mistakes I see and how I avoid them
Even experienced developers make a few mistakes with option(). I still catch myself, so I build guardrails.
Mistake 1: Passing a number as a string.
distance or delay should be a number. If you set them as strings, jQuery UI will coerce them, but you might get ‘10‘ from a data attribute and expect it to be 10. I always parse numeric inputs.
const delayValue = Number($(‘#delayInput‘).val());
$(‘#taskList‘).sortable(‘option‘, ‘delay‘, delayValue);
Mistake 2: Forgetting that false is meaningful.
Options like axis or containment use false to mean “no restriction.” I do not test with if (axis) because false is valid. I always use strict checks.
const axis = $(‘#taskList‘).sortable(‘option‘, ‘axis‘);
const isFree = axis === false;
Mistake 3: Reading options before the widget exists.
Calling option() before you call sortable() will throw an error. I wrap initialization in $(function () { ... }) and call option() only after initialization.
Mistake 4: Changing options while a drag is active.
Changing axis or containment during a drag can cause jitter. I register a start and stop handler and disable certain controls while dragging.
let isDragging = false;
$(‘#taskList‘).sortable({
start: function () { isDragging = true; },
stop: function () { isDragging = false; }
});
$(‘#axisToggle‘).on(‘click‘, function () {
if (isDragging) return; // avoid mid-drag changes
const axis = $(‘#taskList‘).sortable(‘option‘, ‘axis‘);
$(‘#taskList‘).sortable(‘option‘, ‘axis‘, axis === ‘y‘ ? false : ‘y‘);
});
These small rules keep the widget stable and reduce the bugs I see in support tickets.
A practical guardrail: configuration snapshots
When a UI has many options, I like to take a snapshot for debugging and for safe rollback. option() makes this trivial. I keep a small helper that captures the current configuration and then I can restore it later if needed.
function snapshotOptions($el) {
return $.extend(true, {}, $el.sortable(‘option‘));
}
function restoreOptions($el, snapshot) {
$el.sortable(‘option‘, snapshot);
}
const $list = $(‘#taskList‘);
const baseline = snapshotOptions($list);
// ... make changes during a feature experiment ...
restoreOptions($list, baseline);
I rarely need deep cloning, but if you store functions or complex objects in options, a deep clone avoids accidental mutation. It is a small detail that saves time later.
When I choose option() vs rebuild
There are times I do not use option(). If I need to swap the entire set of items, I often rebuild the widget or call destroy() and re-init. That is because option() changes behavior but it does not refresh the DOM structure or event bindings for new elements.
I use a simple checklist:
- I use
option()when behavior changes but the list structure stays in place. - I re-init when I need different items, different containers, or a change in
itemsthat affects selectors. - I re-init when I need to change
connectWithand I want to be confident that all lists reflect the new group.
This is not about performance. It is about correctness. I would rather re-init once than spend hours chasing ghost states.
A traditional vs modern configuration approach
I still see teams with static configuration at init time. In 2026, I prefer a dynamic approach controlled by a state store or feature flags. Here is a direct comparison with concrete differences.
Traditional init-only
—
15–30 minutes in code changes
Hidden until refresh
Requires deploy
Page reload
In my experience, the runtime approach is faster to debug and safer for teams that release weekly or daily. The tradeoff is that you need to manage state, which I handle with a small config object and a single “apply options” function.
A real-world pattern: user-driven preference storage
Here is a pattern I use with localStorage. It reads all options, saves a subset, and re-applies them on load. The key is to only store user-facing options, not internal hooks.
const OPTION_KEYS = [‘axis‘, ‘delay‘, ‘distance‘, ‘disabled‘]; // keep it small
function saveSortablePreferences() {
const options = $(‘#taskList‘).sortable(‘option‘);
const stored = {};
OPTION_KEYS.forEach(key => stored[key] = options[key]);
localStorage.setItem(‘taskSortPrefs‘, JSON.stringify(stored));
}
function loadSortablePreferences() {
const raw = localStorage.getItem(‘taskSortPrefs‘);
if (!raw) return;
const prefs = JSON.parse(raw);
$(‘#taskList‘).sortable(‘option‘, prefs);
}
I pair this with a “restore defaults” button that sets a known safe map. That keeps users in control while giving me a reliable path back to the baseline.
Edge cases: nested lists, dynamic items, and custom helpers
option() works well with complex setups, but I adjust my approach for certain edge cases.
Nested lists: If you have nested ul structures, I avoid changing items via option() unless I can guarantee selectors still match after DOM updates. I often re-init in those cases.
Dynamic items: If list items are injected via an API call, remember that Sortable reads the DOM on initialization. For dynamic changes, you need to call refresh() after new items are inserted. option() will not fix that. I call refresh() after setting options if new items are also added.
$(‘#taskList‘).sortable(‘option‘, ‘items‘, ‘> li‘);
$(‘#taskList‘).sortable(‘refresh‘);
Custom helper: If you switch helper from original to a custom function, be careful with caching. I test the helper function without changing options first, then set it through option() when I know it is stable.
Option() and touch devices
Touch interactions can feel very different from mouse. I often tune options specifically for touch environments when the device has coarse pointers. I keep these settings behind a toggle so I can compare behavior quickly in QA.
function applyTouchTuning() {
$(‘#taskList‘).sortable(‘option‘, {
delay: 150,
distance: 12,
scrollSensitivity: 45
});
}
function applyMouseTuning() {
$(‘#taskList‘).sortable(‘option‘, {
delay: 0,
distance: 4,
scrollSensitivity: 20
});
}
The numbers above are just a starting point. The principle is what matters: higher delay and distance reduce accidental drags on touch, while higher scroll sensitivity helps long lists when your finger is near the edge.
Option() and nested interactive elements
Sortable items often contain buttons, links, and inputs. I use cancel and handle together to prevent accidental drags when a user tries to click a control.
$(‘#taskList‘).sortable({
handle: ‘.drag-handle‘,
cancel: ‘button, input, textarea, a‘
});
If I need to toggle interactivity (for example, in an “edit mode”), I’ll update handle and cancel using option() rather than re-initializing everything. That gives me a clean UX without full rebuilds.
Performance considerations in real apps
The option() method itself is cheap, but the behavior changes can trigger layouts, reflows, or extra DOM reads depending on what you change. I use a few simple performance rules:
- I batch related updates with a single
option()call to reduce intermediate layout work. - I avoid changing geometry options during active drags to prevent forced layout.
- I limit how often I call
option()in response to continuous events likemousemoveorscroll.
In terms of impact, I treat most runtime option updates as “low cost” (sub-millisecond to a couple of milliseconds in common lists), but I expect larger lists or heavier helpers to push that into the 3–12 ms range on average devices. The exact numbers vary, but the principle stands: use option() for discrete changes, not for high-frequency loops.
When I need to change multiple options in response to a slider or live control, I debounce the updates so I only apply the final values after the user stops dragging the control.
let pending = null;
$(‘#distanceSlider‘).on(‘input‘, function () {
const value = Number(this.value);
clearTimeout(pending);
pending = setTimeout(() => {
$(‘#taskList‘).sortable(‘option‘, ‘distance‘, value);
}, 80);
});
That small delay improves responsiveness without sacrificing control.
A production pattern: centralized configuration
To keep things sane in larger apps, I centralize Sortable options in a plain object and use option() as the renderer. This makes it easy to log changes, sync with user preferences, and avoid scattered logic.
const sortableConfig = {
axis: ‘y‘,
delay: 0,
distance: 4,
disabled: false
};
function applySortableConfig() {
$(‘#taskList‘).sortable(‘option‘, sortableConfig);
}
function setSortableOption(key, value) {
sortableConfig[key] = value;
applySortableConfig();
}
This is a small abstraction, but it makes runtime updates predictable and reduces accidental divergence between your state and the widget state.
Troubleshooting with option() snapshots
When a bug report says “dragging is weird,” I pull an options snapshot and compare it to the expected baseline. The differences usually reveal the issue quickly.
function diffOptions(current, baseline) {
const changes = {};
Object.keys(baseline).forEach(key => {
if (current[key] !== baseline[key]) changes[key] = [baseline[key], current[key]];
});
return changes;
}
const baseline = $(‘#taskList‘).sortable(‘option‘);
// ... later
const current = $(‘#taskList‘).sortable(‘option‘);
console.log(diffOptions(current, baseline));
I don’t ship this in production, but I keep it in my debugging toolkit. It turns a vague “feels off” problem into something concrete.
Testing strategies for dynamic options
If your app uses option() heavily, you should test it. I focus on two levels:
- Unit tests for state updates: when you toggle a UI control, does the right option get set?
- Integration tests for user experience: can the user drag in the configured state?
A tiny test-like snippet for unit-level checks:
function expectOption($el, key, value) {
const actual = $el.sortable(‘option‘, key);
if (actual !== value) throw new Error(Expected ${key}=${value}, got ${actual});
}
setSortableOption(‘axis‘, ‘y‘);
expectOption($(‘#taskList‘), ‘axis‘, ‘y‘);
Even without a test runner, these checks help you verify configuration behavior during development.
When I avoid option() entirely
There are cases where I intentionally avoid option() and go straight to destroy() and re-init:
- The list structure or selector logic changes drastically.
- I swap between fundamentally different layouts (grid vs list).
- I need to change
connectWithacross multiple lists with complex conditional rules. - I have deeply nested lists and
itemschanges are frequent.
In those cases, a clean re-init is clearer and more reliable than trying to modify the live widget. It also avoids subtle state problems that come from partial updates.
Alternative approaches that still use option() under the hood
Sometimes I wrap option() in a higher-level API. This is a good compromise between flexibility and safety.
A mode-based API
Instead of exposing every option, I define a small set of modes. Each mode maps to a specific option bundle.
const MODE_MAP = {
compact: { distance: 2, delay: 0 },
safe: { distance: 12, delay: 150 }
};
function applyMode(mode) {
$(‘#taskList‘).sortable(‘option‘, MODE_MAP[mode]);
}
A feature-flag API
If you have feature flags or experiment toggles, you can store them in a shared config and derive options from them.
const flags = {
preciseDragging: false,
axisLocked: true
};
function computeOptionsFromFlags() {
return {
distance: flags.preciseDragging ? 12 : 4,
axis: flags.axisLocked ? ‘y‘ : false
};
}
function applyFlags() {
$(‘#taskList‘).sortable(‘option‘, computeOptionsFromFlags());
}
These patterns give you the benefits of option() while keeping runtime changes organized.
A practical reset pattern that users trust
In complex UIs, users need a way to get back to a known good state. I provide a “reset” button that simply re-applies a default map. Because I use option() in other places, this reset works immediately and predictably.
const DEFAULTS = {
axis: ‘y‘,
delay: 0,
distance: 4,
disabled: false
};
$(‘#resetButton‘).on(‘click‘, function () {
$(‘#taskList‘).sortable(‘option‘, DEFAULTS);
});
A reset button is also a great fallback when you roll out new experiments. Users trust it because it feels deterministic.
Final checklist I keep nearby
When I am deep into a drag-and-drop interface and I start touching runtime options, I run through this mental checklist:
- Is the widget initialized before I call
option()? - Am I changing a structural option that requires
refresh()or re-init? - Am I changing options during an active drag?
- Do I need to update UI messaging to match the new behavior?
- Do I have a reset path if this change causes confusion?
These five questions save me from most production bugs.
Wrap-up: why option() is worth the mental model
The option() method is not just a convenience; it is how you keep Sortable adaptable without turning your code into a tangle of destroy/re-init logic. I use it for live UX tuning, accessibility improvements, controlled experiments, and safe defaults. I avoid it only when structural changes make a rebuild safer.
If you are already using Sortable, adding a simple option()-based control layer will pay off quickly. You will ship changes faster, support will be easier, and your UI will feel more intentional because you can tune it in real time instead of guessing at initialization time.


