Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions src/actions/element.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import ActionBase from "./base";
import debounce from "./../helpers/debounce";

class Element extends ActionBase {
add() {
const target = this.targets[0];
if (!target) return;

const sourceSelector = this.currentElement.dataset.addSource;
if (!sourceSelector) return;

const source = document.querySelector(sourceSelector);
if (!source) return;

const clonedElement = this.#cloneSource(source);
const position = this.currentElement.dataset.addAt || "beforeend";

target.insertAdjacentElement(position, clonedElement);
}

remove() {
const delay = this.currentElement.dataset.delay;

const removeElements = () => {
this.targets.forEach((target) => target.remove());
};

if (delay) {
debounce(removeElements, parseInt(delay));
} else {
removeElements();
}
}

// private

#cloneSource(source) {
return source.tagName === "TEMPLATE"
? source.content.cloneNode(true).firstElementChild
: source.cloneNode(true);
}
}

export const action =
(method) =>
(element, options = {}) => {
const instance = new Element(element, options);

return instance[method]();
};

export default {
add: action("add"),
remove: action("remove")
};
2 changes: 2 additions & 0 deletions src/actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import clipboardActions from "./clipboard";
import confirmActions from "./confirm";
import dataAttributeActions from "./data_attribute";
import dialogActions from "./dialog";
import elementActions from "./element";
import formActions from "./form";
import intersectionActions from "./intersection";
import reloadActions from "./reload";
Expand All @@ -17,6 +18,7 @@ export const actions = {
confirm: confirmActions,
dataAttribute: dataAttributeActions,
dialog: dialogActions,
element: elementActions,
form: formActions,
intersection: intersectionActions,
reload: reloadActions,
Expand Down
206 changes: 206 additions & 0 deletions test/element.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
<!doctype html>
<html>
<head>
<title>Attractive.js - Element Tests</title>
<link rel="stylesheet" href="./styles.css" />
<script src="../dist/attractive.js"></script>
</head>
<body>
<h1>Element</h1>
<p><a href="index.html">← Back to test index</a></p>

<div class="test-section">
<h2>Remove element (self)</h2>

<button data-action="element#remove">
Remove this button (click me)
</button>
</div>

<div class="test-section">
<h2>Remove element with target</h2>

<button data-action="element#remove" data-target="#removable-item">
Remove target element
</button>

<p id="removable-item" style="padding: 1rem; background: #ffebee;">
This element will be removed when the button is clicked.
</p>
</div>

<div class="test-section">
<h2>Remove element with delay</h2>

<button
data-action="element#remove"
data-target="#delayed-item"
data-delay="2000"
>
Remove after 2 seconds
</button>

<p id="delayed-item" style="padding: 1rem; background: #fff3e0;">
This element will be removed 2 seconds after clicking the button.
</p>
</div>

<div class="test-section">
<h2>Remove multiple elements</h2>

<button data-action="element#remove" data-target=".removable-card">
Remove all cards
</button>

<div style="display: flex; gap: 1rem; margin-top: 1rem;">
<div
class="removable-card"
style="padding: 1rem; background: #e3f2fd; border-radius: 4px;"
>
Card 1
</div>
<div
class="removable-card"
style="padding: 1rem; background: #e3f2fd; border-radius: 4px;"
>
Card 2
</div>
<div
class="removable-card"
style="padding: 1rem; background: #e3f2fd; border-radius: 4px;"
>
Card 3
</div>
</div>
</div>

<div class="test-section">
<h2>Add element - beforeend (default)</h2>

<button
data-action="element#add"
data-target="#list-1"
data-add-source="#item-template"
>
Add item
</button>

<ul
id="list-1"
style="padding: 1rem; background: #f5f5f5; border-radius: 4px; min-height: 3rem;"
>
<li style="margin-bottom: 0.5rem;">Existing item</li>
</ul>

<template id="item-template">
<li style="margin-bottom: 0.5rem; color: #1976d2;">New item</li>
</template>
</div>

<div class="test-section">
<h2>Add element - afterbegin</h2>

<button
data-action="element#add"
data-target="#list-2"
data-add-source="#item-template-2"
data-add-at="afterbegin"
>
Add item at start
</button>

<ul
id="list-2"
style="padding: 1rem; background: #f5f5f5; border-radius: 4px; min-height: 3rem;"
>
<li style="margin-bottom: 0.5rem;">Existing item</li>
</ul>

<template id="item-template-2">
<li style="margin-bottom: 0.5rem; color: #388e3c;">New item (prepended)</li>
</template>
</div>

<div class="test-section">
<h2>Add element - beforebegin</h2>

<button
data-action="element#add"
data-target="#target-element"
data-add-source="#card-template"
data-add-at="beforebegin"
>
Add card before
</button>

<div
id="target-element"
style="padding: 1rem; background: #fff3e0; border-radius: 4px; margin-top: 1rem;"
>
<strong>Target Element</strong> - New cards appear before this
</div>

<template id="card-template">
<div
style="padding: 1rem; background: #e1f5fe; border-radius: 4px; margin-bottom: 0.5rem;"
>
<strong>New Card</strong> - Inserted before target
</div>
</template>
</div>

<div class="test-section">
<h2>Add element - afterend</h2>

<button
data-action="element#add"
data-target="#target-element-2"
data-add-source="#card-template-2"
data-add-at="afterend"
>
Add card after
</button>

<div
id="target-element-2"
style="padding: 1rem; background: #f3e5f5; border-radius: 4px; margin-top: 1rem;"
>
<strong>Target Element</strong> - New cards appear after this
</div>

<template id="card-template-2">
<div
style="padding: 1rem; background: #fff9c4; border-radius: 4px; margin-top: 0.5rem;"
>
<strong>New Card</strong> - Inserted after target
</div>
</template>
</div>

<div class="test-section">
<h2>Add from regular element (not template)</h2>

<button
data-action="element#add"
data-target="#clone-container"
data-add-source="#source-div"
>
Clone and add
</button>

<div
id="source-div"
style="padding: 1rem; background: #e8f5e9; border-radius: 4px; margin: 1rem 0; display: inline-block;"
>
<strong>Source Element</strong> - This gets cloned
</div>

<div
id="clone-container"
style="padding: 1rem; background: #f5f5f5; border-radius: 4px; min-height: 3rem;"
>
<p style="margin: 0 0 0.5rem 0; font-weight: bold;">Clones appear here:</p>
</div>
</div>
</body>
</html>
1 change: 1 addition & 0 deletions test/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ <h1>Attractive.js test suite</h1>
<li><a href="confirm.html">Confirm</a></li>
<li><a href="data_attribute.html">Data Attribute</a></li>
<li><a href="dialog.html">Dialog</a></li>
<li><a href="element.html">Element</a></li>
<li><a href="form.html">Form</a></li>
<li><a href="intersection.html">Intersection</a></li>
<li><a href="reload.html">Reload</a></li>
Expand Down