{"record":{"id":"ead2864c0884163a","repo":"zenorocha/clipboard.js","slug":"invalid-target-attribute-please-use-readonly","errorCode":null,"errorMessage":"Invalid \"target\" attribute. Please use \"readonly\" instead of \"disabled\" attribute","messagePattern":"Invalid \"target\" attribute\\. Please use \"readonly\" instead of \"disabled\" attribute","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/actions/default.js","lineNumber":22,"sourceCode":"/**\n * Inner function which performs selection from either `text` or `target`\n * properties and then executes copy or cut operations.\n * @param {Object} options\n */\nconst ClipboardActionDefault = (options = {}) => {\n  // Defines base properties passed from constructor.\n  const { action = 'copy', container, target, text } = options;\n\n  // Sets the `action` to be performed which can be either 'copy' or 'cut'.\n  if (action !== 'copy' && action !== 'cut') {\n    throw new Error('Invalid \"action\" value, use either \"copy\" or \"cut\"');\n  }\n\n  // Sets the `target` property using an element that will be have its content copied.\n  if (target !== undefined) {\n    if (target && typeof target === 'object' && target.nodeType === 1) {\n      if (action === 'copy' && target.hasAttribute('disabled')) {\n        throw new Error(\n          'Invalid \"target\" attribute. Please use \"readonly\" instead of \"disabled\" attribute'\n        );\n      }\n\n      if (\n        action === 'cut' &&\n        (target.hasAttribute('readonly') || target.hasAttribute('disabled'))\n      ) {\n        throw new Error(\n          'Invalid \"target\" attribute. You can\\'t cut text from elements with \"readonly\" or \"disabled\" attributes'\n        );\n      }\n    } else {\n      throw new Error('Invalid \"target\" value, use a valid Element');\n    }\n  }\n\n  // Define selection strategy based on `text` property.","sourceCodeStart":4,"sourceCodeEnd":40,"githubUrl":"https://github.com/zenorocha/clipboard.js/blob/899378dee9681dcf4cb3d702c23a3f3cd9f473d8/src/actions/default.js#L4-L40","documentation":"Thrown when `action` is 'copy' and the target DOM element carries a `disabled` attribute. Disabled form controls are not selectable and have no selectable value, so the library refuses to copy from them and tells you to use `readonly` instead, which keeps the field selectable while still non-editable.","triggerScenarios":"Copying from an `<input>`/`<textarea>` that is `<input disabled ...>` with `new ClipboardJS(el, { text: () => field.value })` won't hit this, but using a `target` selector pointing at `document.querySelector('input[disabled]')` with the default copy action will; binding `data-clipboard-target=\"#myDisabled\"` on a trigger where `#myDisabled` is a disabled input; copying directly via `ClipboardActionDefault({ action: 'copy', target: disabledEl })`.","commonSituations":"Form fields that toggle between disabled and readonly states (e.g., a disabled submit-time input the user wants to copy); copy buttons on grayed-out fields in admin UIs; migrating from native `disabled` styling to clipboard copy without changing the attribute; ARIA-correct disabled controls that also set the HTML disabled attribute.","solutions":["Replace the `disabled` attribute on the target with `readonly` — readonly fields remain selectable and copyable while preventing edits.","If the field must stay disabled for form-submission reasons, copy via the `text` callback instead of the `target` selector: `new ClipboardJS(btn, { text: () => field.value })`.","Dynamically swap `disabled` for `readonly` (or remove it) in the trigger's click handler before the clipboard action runs.","If disabled state is essential, render a separate non-disabled mirror element (e.g., a read-only span) and point the target at it."],"exampleFix":"// before\n<input id=\"token\" value=\"abc123\" disabled />\n<button class=\"btn\" data-clipboard-target=\"#token\">Copy</button>\n// after\n<input id=\"token\" value=\"abc123\" readonly />\n<button class=\"btn\" data-clipboard-target=\"#token\">Copy</button>","handlingStrategy":"validation","validationCode":"function canCopyFrom(el) {\n  if (!el || el.nodeType !== 1) return false;\n  if (el.hasAttribute('disabled')) return false;\n  return true;\n}\nconst el = document.querySelector('#token');\nif (!canCopyFrom(el)) {\n  // use text callback or swap to readonly before copying\n  new ClipboardJS(btn, { text: () => el.value });\n} else {\n  new ClipboardJS(btn, { target: () => el });\n}","typeGuard":"/**\n * @param {Element} el\n * @returns {boolean}\n */\nfunction isCopyableElement(el) {\n  return el instanceof Element && el.nodeType === 1 && !el.hasAttribute('disabled');\n}","tryCatchPattern":"try {\n  ClipboardActionDefault({ action: 'copy', target: el });\n} catch (err) {\n  if (/use \"readonly\" instead of \"disabled\"/.test(err.message)) {\n    // fall back to reading the value directly via the text path\n    ClipboardActionCopy(el.value, { container });\n  } else {\n    throw err;\n  }\n}","preventionTips":["Prefer `readonly` over `disabled` for any field a user might copy from.","Bind copy buttons via the `text` callback when the source field's editable state is dynamic.","Audit copy targets at render time and disable the copy button itself when the field is disabled.","Keep a single source of truth for which fields are editable vs. selectable."],"tags":["clipboard","dom","form-controls","attribute-mismatch"],"backgroundTag":null,"analyzedSha":"899378dee9681dcf4cb3d702c23a3f3cd9f473d8","analyzedAt":"2026-08-13T04:33:09.334Z","schemaVersion":2},"datasetVersion":"2026-08-13T09:17:06.757Z"}