Copy Markup
Copy input, select or other markups.
Contents
- Overview
- Installation
- Basic usage
- Configuration Options
- JavaScript API
- Events
- Common Patterns
- License
Overview
The Copy Markup component allows you to dynamically duplicate HTML elements (inputs, selects, or other markup) with a single click. It's useful for creating dynamic forms where users can add multiple instances of the same field.
Key Features:
- One-click element duplication
- Configurable copy limits
- Automatic element management
- Programmatic control via JavaScript API
- Event system for copy/delete tracking
- Support for any HTML markup
Installation
To get started, install Copy Markup plugin via npm, else you can skip this step if you are already using Preline UI as a package.
npm i @preline/copy-markup
CSS
@import the plugin's CSS file into your Tailwind CSS file.
@import "tailwindcss";
/* @preline/copy-markup */
@import "./node_modules/@preline/copy-markup/theme.css";
JavaScript
Include the JavaScript that powers the interactive elements near the end of your </body> tag:
<script src="./node_modules/@preline/copy-markup/index.js"></script>
Manual Initialization
Use the non-auto entry if you need manual initialization. In this mode, automatic initialization on page load is not included, so the component should be initialized explicitly.
<script type="module">
import HSCopyMarkup from "@preline/copy-markup/non-auto.mjs";
new HSCopyMarkup(document.querySelector("#copy-markup"));
</script>
Via Bundler
When using a bundler (Vite, webpack, etc.), import the plugin directly as an ES module.
@preline/copy-markup is the auto-init entry: it scans the DOM and initializes matching elements automatically.
import "@preline/copy-markup";
@preline/copy-markup/non-auto is the manual entry: use it when you want explicit control over when initialization happens, either via autoInit() or by creating a specific instance yourself.
import HSCopyMarkup from "@preline/copy-markup/non-auto";
HSCopyMarkup.autoInit();
// Or initialize a specific element manually
const el = document.querySelector("#copy-markup");
if (el) new HSCopyMarkup(el);
TypeScript
This package ships with TypeScript type definitions. No additional @types/ package is needed.
Basic usage
The following example demonstrates the minimal HTML structure required for a copy markup component. This is a base template without custom styling - you can apply your own CSS classes and styles as needed. Clicking the button will duplicate the target element.
<div id="hs-wrapper-for-copy-first">
<input id="hs-content-for-copy-first" type="text" class="py-3 px-4 block w-full border-gray-200 rounded-lg text-sm text-gray-800 focus:z-10 dark:bg-neutral-800 dark:border-neutral-700 dark:text-neutral-200 dark:placeholder:text-neutral-500" placeholder="Enter Name">
</div>
<button type="button" data-hs-copy-markup='{
"targetSelector": "#hs-content-for-copy-first",
"wrapperSelector": "#hs-wrapper-for-copy-first",
"limit": 3
}' id="hs-copy-content-first">
Add Name
</button>
Structure Requirements:
data-hs-copy-markup: Required on the trigger button, contains configuration options as JSONtargetSelector: Required, must be a valid CSS selector pointing to the element to copywrapperSelector: Required, must be a valid CSS selector pointing to the container where copies will be added- Target element must exist in the DOM
Initial State:
- One instance of the target element exists
- Copy button is enabled (unless limit is reached)
Configuration Options
Data Options
Data options are specified in the data-hs-copy-markup attribute as a JSON object.
| Option | Target Element | Type | Default | Description |
|---|---|---|---|---|
data-hs-copy-markup |
Trigger button | - | - | Activate a Copy Markup by specifying on an element. Should be added to the button (trigger). |
:targetSelector |
Inside data-hs-copy-markup |
string (CSS selector) | - | Specifies the target element to copy. The value must be a valid CSS selector. Required. |
:wrapperSelector |
Inside data-hs-copy-markup |
string (CSS selector) | - | Specifies which element should be used for copying. The value must be a valid CSS selector. Required. |
:limit |
Inside data-hs-copy-markup |
number | null |
Specifies how many items you can copy until the button is disabled. null means no limit. |
Example:
<button data-hs-copy-markup='{
"targetSelector": "#hs-input-field",
"wrapperSelector": "#hs-wrapper",
"limit": 5
}'>
Add Field
</button>
JavaScript API
The HSCopyMarkup object is available in the global window object after the plugin is loaded.
Instance Methods
These methods are called on a copy markup instance.
| Method | Parameters | Return Type | Description |
|---|---|---|---|
delete(target) |
target: HTMLElement |
void |
Remove the element associated to the target. The target should be a Node (HTMLElement). Use this to programmatically remove copied elements. |
destroy() |
None | void |
Destroys the copy markup instance, removes all generated markup, classes, and event listeners. Use when removing copy markup from DOM. |
Static Methods
These methods are called directly on the HSCopyMarkup class.
| Method | Parameters | Return Type | Description |
|---|---|---|---|
HSCopyMarkup.getInstance(target, isInstance) |
target: HTMLElement | string (CSS selector)isInstance: boolean (optional) |
HSCopyMarkup | { id: string | number, element: HSCopyMarkup } | null |
Returns the copy markup instance associated with the target. If isInstance is true, returns collection item object { id, element } where element is the HSCopyMarkup instance. If isInstance is false or omitted, returns the HSCopyMarkup instance directly. Returns null if copy markup instance is not found. |
Usage Examples
Example 1: Deleting copied elements
// Get the copy markup instance
const instance = HSCopyMarkup.getInstance('#hs-copy-content', true);
if (instance) {
const { element } = instance;
const deleteBtn = document.querySelector('#hs-delete-btn');
deleteBtn.addEventListener('click', () => {
// Delete a specific copied element
const copiedElement = document.querySelector('#hs-copy-markup-item-first');
if (copiedElement) {
element.delete(copiedElement);
}
});
}
Example 2: Using instance methods (recommended pattern)
// Get the copy markup instance
const instance = HSCopyMarkup.getInstance('#hs-copy-content', true);
if (instance) {
const { element } = instance;
// Access instance properties
console.log('Items:', element.items);
console.log('Limit:', element.limit);
// Delete a copied element programmatically
function removeCopiedItem(itemElement) {
element.delete(itemElement);
}
// Clean up when removing from DOM
function removeCopyMarkup() {
element.destroy();
}
}
Example 3: Destroying copy markup instance
const instance = HSCopyMarkup.getInstance('#hs-copy-content', true);
if (instance) {
const { element } = instance;
const removeBtn = document.querySelector('#hs-remove-btn');
removeBtn.addEventListener('click', () => {
// Clean up before removing from DOM
element.destroy();
// Now safe to remove the element
document.querySelector('#hs-copy-content').remove();
});
}
Events
Copy markup instances emit events that can be listened to for copy/delete tracking and custom behavior.
| Event Name | When Fired | Callback Parameter | Description |
|---|---|---|---|
on:copy |
When target element was copied | HTMLElement (copied element) |
Fires when a new copy of the target element is created. Returns the newly created element. |
on:delete |
When target element was deleted | HTMLElement (deleted element) |
Fires when a copied element is deleted. Returns the deleted element. |
Event Usage Example
// Get copy markup instance
const instance = HSCopyMarkup.getInstance('#hs-copy-content', true);
if (instance) {
const { element } = instance;
// Listen to copy event
element.on('copy', (copiedElement) => {
console.log('Element copied:', copiedElement);
// Perform actions after element is copied
// e.g., initialize other plugins on the new element, update counters
});
// Listen to delete event
element.on('delete', (deletedElement) => {
console.log('Element deleted:', deletedElement);
// Perform cleanup or state updates
});
}
Common Patterns
Pattern 1: Limited Copies
Limit the number of copies that can be created.
<button data-hs-copy-markup='{
"targetSelector": "#hs-field",
"wrapperSelector": "#hs-wrapper",
"limit": 5
}'>
Add Field (Max 5)
</button>
Pattern 2: Dynamic Form Fields
Create dynamic form fields with copy functionality.
<div id="hs-fields-wrapper">
<input id="hs-field-template" type="text" placeholder="Field name">
</div>
<button data-hs-copy-markup='{
"targetSelector": "#hs-field-template",
"wrapperSelector": "#hs-fields-wrapper"
}'>
Add Another Field
</button>
License
Copyright (c) 2024 Htmlstream.
Licensed under the MIT License.