Tiny JavaScript Library for Dynamic Element Cloning – Kloner

Category: Javascript | April 17, 2025
Author:fivefifteen
Views Total:0 views
Official Page:Go to website
Last Update:April 17, 2025
License:MIT

Preview:

Tiny JavaScript Library for Dynamic Element Cloning – Kloner

Description:

Kloner is a lightweight and customizable JavaScript library that allows you to duplicate/repeat elements (element group) with no dependencies.

It can be useful when working with form fields that need to be duplicated, such as multiple address inputs, contact information entries, or any repeatable blocks of content.

Features:

  • Easy Setup: Just requires adding specific data attributes to your HTML.
  • Automatic Indexing: Handles updating {kloner-index} and {kloner-number} placeholders in cloned elements, crucial for form field names and IDs.
  • Configurable: Options available via JavaScript initialization or data-kloner-* attributes.
  • Lifecycle Callbacks: Provides hooks like beforeAdd, afterAdd, beforeRemove, afterRemove for custom logic.
  • Min/Max Limits: Control the minimum and maximum number of cloned items.

See it in action:

How to use it:

1. Install and import Kloner with NPM.

# NPM
$ npm install kloner
// ES6
import kloner from 'kloner'
// CommonJS
const kloner = require('kloner')

2. Or load the kloner.min.js script in the document.

<script  src="kloner.min.js"></script>

3. Initialize Kloner once the DOM is ready.

window.addEventListener('load', function () {
  kloner()
})

4. To create a cloneable element, you need a container element, a template element inside it (the one to be cloned), and add/remove trigger buttons.

  • The container (div#people) uses the kloner class. Kloner will automatically find it.
  • data-kloner-child-selector=".person" tells Kloner which element inside the container is the template. If omitted, it tries [data-kloner-template] or the first direct child.
  • Placeholders like {kloner-index} and {kloner-number} inside the template (.person) are automatically replaced with the correct zero-based index and one-based number when cloned. This is essential for form field name and id attributes.
  • Buttons with data-kloner-add and data-kloner-remove attributes handle the cloning and removal actions. The remove button typically sits inside the cloned element.
<div id="people" class="container people-grid kloner" data-kloner-child-selector=".person">
  <div class="person">
    <p>Person #{kloner-number}</p>
    <fieldset>
      <label for="people[{kloner-index}][first_name]">First Name</label>
      <input type="text" id="people[{kloner-index}][first_name]" name="people[{kloner-index}][first_name]" />
    </fieldset>
    <fieldset>
      <label for="people[{kloner-index}][last_name]">Last Name</label>
      <input type="text" id="people[{kloner-index}][last_name]" name="people[{kloner-index}][last_name]" />
    </fieldset>
    <fieldset>
      <label for="people[{kloner-index}][age]">Age</label>
      <input type="number" id="people[{kloner-index}][age]" name="people[{kloner-index}][age]" min="1" max="99" />
    </fieldset>
    <button type="button" class="btn btn-custom" data-kloner-remove>Remove Person</button>
  </div>
  <div class="btn-container">
    <button type="button" class="btn btn-custom" data-kloner-add>Add Person</button>
  </div>
</div>

5. Configure Kloner during initialization or via data-kloner-* attributes on the container element.

  • containerSelector: '[data-kloner], .kloner'
    • CSS selector used to find the main container elements that Kloner should manage.
  • childSelector: '[data-kloner-template], :scope > *'
    • CSS selector used within each container to identify the template element that will be cloned. :scope refers to the container itself.
  • min: 0
    • The minimum number of cloned items that must remain. Kloner prevents removal below this count.
  • max: null
    • The maximum number of cloned items allowed. Kloner prevents adding more items beyond this limit. null means no limit.
  • start: 0
    • Specifies the initial number of items to create when Kloner initializes. Can be an integer for empty clones or an array of parameter objects to populate initial clones with specific data.
  • template: null
    • Allows providing an HTML string directly as the template, bypassing the need for a template element within the container specified by childSelector.
  • updateChildren: false
    • If set to true, Kloner will re-evaluate and update the {kloner-index} and {kloner-number} placeholders in all existing child elements after any add or remove operation. Can impact performance with many items.
  • parameters: null
    • An object containing custom key-value pairs. These are used to replace corresponding {kloner-yourKey} placeholders within the template HTML.
  • beforeAdd: null
    • A callback function executed just before a new element is added to the DOM. Receives (container, newElement, options). Returning false cancels the add operation.
  • afterAdd: null
    • A callback function executed immediately after a new element has been added to the DOM. Receives (container, newElement, options). Useful for initializing third-party scripts on the new element.
  • beforeRemove: null
    • A callback function executed just before an element is removed. Receives (container, elementToRemove, options). Returning false cancels the remove operation.
  • afterRemove: null
    • A callback function executed immediately after an element has been removed from the DOM. Receives (container, removedElement, options).
  • beforeChildUpdate: null
    • A callback function executed before updating placeholders on an existing child element, only relevant if updateChildren is true. Receives (container, element, options). Returning false skips the update for this specific element.
  • afterChildUpdate: null
    • A callback function executed after updating placeholders on an existing child element, only relevant if updateChildren is true. Receives (container, element, options).
kloner({
  afterAdd: null,
  afterChildUpdate: null,
  afterRemove: null,
  beforeAdd: null,
  beforeChildUpdate: null,
  beforeRemove: null,
  childSelector: '[data-kloner-template], :scope > *',
  containerSelector: '[data-kloner], .kloner',
  max: null,
  min: 0,
  parameters: null,
  start: 0,
  template: null,
  updateChildren: false
})

6. API methods.

// Adds a new element at the specified index
kloner.add(container, [index], [options])
// Removes an element at the specified index
kloner.remove(container, [index], [options])
// Gets Kloner instances matching the selector
kloner.getInstances(selector, [verify], [single])
// Updates indices and numbers for all children
kloner.updateChildren(container)
// Replaces placeholders with actual values
kloner.replaceParameters(element, parameters)

FAQs

Q: How do I handle event listeners on cloned elements? My date picker / select library doesn’t work.
A: Standard cloneNode(true) doesn’t copy event listeners attached via addEventListener. You need to initialize any JS components or attach listeners after the element is cloned and added to the DOM. The afterAdd callback is the perfect place for this. Pass the newElement argument from the callback to your initialization function (e.g., new DatePicker(newElement.querySelector('.datepicker'))).

Q: How can I pass initial data or different values into each cloned item?
A: Use the start option during initialization. If you provide an array of objects to start, Kloner will call add for each object, passing the object as parameters. You can then use custom placeholders like {kloner-firstName} in your template, and Kloner will replace them with values from the corresponding object in the start array. Example: start: [{firstName: 'John'}, {firstName: 'Jane'}]. You can also call container.kloner.add(null, { parameters: { yourKey: 'yourValue' } }) programmatically.

Q: What if my template element needs to be hidden initially?
A: You can hide the template element using CSS (e.g., display: none;). Kloner clones it from memory, so its initial visibility state doesn’t prevent cloning. Alternatively, mark it with data-kloner-template. Kloner automatically removes elements matching [data-kloner-template] after identifying them during initialization.

Q: Is it possible to reset a Kloner container back to its initial state?
A: There isn’t a built-in reset() method. You’d typically remove all cloned items programmatically by repeatedly calling container.kloner.remove() until the minimum count is reached, or manually remove the elements via DOM manipulation and then potentially re-initialize Kloner on the container if needed.

You Might Be Interested In:


Leave a Reply