A simple, yet highly customizable jQuery plugin to handle all of your client-side repetitive DOM needs. Simple, quick and powerful templating using Mustache syntax. Modeled after ng-repeat with automatic DOM synchronization.
View Interactive Demos - See jq-repeat in action with live examples!
- jQuery 3.x (http://jquery.com/)
- Mustache 4.x (https://github.com/janl/mustache.js)
npm install jq-repeat<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mustache.js/4.2.0/mustache.min.js"></script>
<script src="dist/js/jq-repeat.min.js"></script>Add a jq-repeat attribute with a unique scope name to any element you want to use as a repeating template:
<ul>
<li jq-repeat="toDo">
<span class="item">{{ item }}</span>
<span class="status">{{ done }}</span>
</li>
</ul>Use Mustache syntax ({{ variable }}) for text interpolation and {{{ variable }}} for unescaped HTML.
Items are automatically rendered when you push data to the scope:
$.scope.toDo.push({ item: 'Get milk', done: 'Yes' });
// Add multiple items at once
$.scope.toDo.push(
{ item: 'Collect underwear', done: 'Yes' },
{ item: '?', done: 'No' },
{ item: 'Profit', done: 'No' }
);Sort items automatically with the jr-order-by attribute:
<li jq-repeat="users" jr-order-by="name">
{{ name }} - {{ age }}
</li>
<li jq-repeat="scores" jr-order-by="points" jr-order-reverse="true">
{{ player }}: {{ points }}
</li>When jr-order-by is set, items are automatically inserted and maintained in sorted order.
Use a custom property as the index with jq-index-key:
<div jq-repeat="users" jq-index-key="userId">
{{ userName }}
</div>$.scope.users.push({ userId: 'user123', userName: 'John' });
$.scope.users.update('userId', 'user123', { userName: 'John Doe' });jq-repeat supports nested repeating templates:
<div jq-repeat="departments">
<h2>{{ name }}</h2>
<ul>
<li jq-repeat="employees">
{{ firstName }} {{ lastName }}
</li>
</ul>
</div>Access parent data in nested templates using _parent:
<li jq-repeat="employees">
{{ firstName }} works in {{ _parent.name }}
</li>The scope object extends Array, so all standard array methods work with automatic DOM synchronization:
Add items to the end (or in sorted position if jr-order-by is set):
$.scope.toDo.push({ item: 'New task', done: 'No' });Remove and return the last item:
const last = $.scope.toDo.pop();Remove and return the first item:
const first = $.scope.toDo.shift();Add items to the beginning (or in sorted position if jr-order-by is set):
$.scope.toDo.unshift({ item: 'Urgent task', done: 'No' });Remove and/or add items at a specific position:
// Remove 2 items starting at index 1
$.scope.toDo.splice(1, 2);
// Replace 1 item at index 2
$.scope.toDo.splice(2, 1, { item: 'Updated task', done: 'Yes' });
// With custom index key
$.scope.users.splice('user123', 1); // Remove user with userId 'user123'Reverse the order of items:
$.scope.toDo.reverse();Find the index of an item:
// With custom index key
const index = $.scope.users.indexOf('user123');
// By property value
const index = $.scope.users.indexOf('name', 'John');
// By object reference
const index = $.scope.users.indexOf(userObject);Update an item with automatic DOM re-rendering (throttled at 50ms):
// Update by index
$.scope.toDo.update(0, { done: 'Yes' });
// Update with custom index key
$.scope.users.update('user123', { userName: 'Jane Doe' });
// Update by key/value pair
$.scope.items.update('id', 42, { quantity: 10 });Get an item by key/value:
const user = $.scope.users.getByKey('userId', 'user123');Remove an item:
// By custom index
$.scope.users.remove('user123');
// By key/value
$.scope.items.remove('id', 42);Remove all items:
$.scope.toDo.empty();Get the RepeatList scope for an element:
$('.jq-repeat-toDo').first().scopeGet(); // Returns $.scope.toDoGet the closest jq-repeat element:
$('.item').scopeGetEl(); // Returns the <li jq-repeat-scope="toDo"> elementGet the data object for a specific rendered item:
const itemData = $('.jq-repeat-toDo').first().scopeItem();Update a specific item:
$('.jq-repeat-toDo').first().scopeItemUpdate({ done: 'Yes' });Remove a specific item:
$('.jq-repeat-toDo').first().scopeItemRemove();Called when an item is added to the DOM. Default implementation just shows the element:
$.scope.toDo.__put = function($el, item, list) {
$el.fadeIn(300);
};Called when an item is removed from the DOM:
$.scope.toDo.__take = function($el, item, list) {
$el.fadeOut(300, function() {
$(this).remove();
});
};Called when an item is updated:
$.scope.toDo.__putUpdate = function($oldEl, $newEl, item, list) {
$oldEl.fadeOut(150, function() {
$newEl.fadeIn(150);
$oldEl.replaceWith($newEl);
});
};Transform data before rendering:
$.scope.toDo.parseKeys.timestamp = function(value, key, data) {
return new Date(value).toLocaleString();
};Special variables available in templates:
_parent- Access parent scope data in nested templates_index- Current item's index in the array_list- Reference to the RepeatList instancenestedTemplates- Array of nested template HTML
Build the distribution files:
npm run buildThis creates:
dist/js/jq-repeat.js- Unminified versiondist/js/jq-repeat.min.js- Minified version with source map
Run tests:
npm testWorks in all modern browsers that support ES6 features including:
- Classes
- Arrow functions
- Proxies
- Template literals
- Spread operator
For older browsers, use a transpiler like Babel.
- Written by William Mantly
- Big thanks to Derek Hu for creating NPM and bower package, and other general housekeeping
- Also, thanks to Raja Kapur for advice and guidance
MIT License - see LICENSE file for details