import { ref, watch } from 'vue';
function useElementSize(element) {
const width = ref(0);
const height = ref(0);
let observer = null;
function disconnect() {
if (observer !== null) {
observer.disconnect();
observer = null;
}
}
function connect(element) {
disconnect();
observer = new ResizeObserver((entries) => {
const rect = entries[0]?.contentRect;
if (rect) {
width.value = rect.width;
height.value = rect.height;
}
});
observer.observe(element);
}
watch(
element,
(el) => {
if (el) connect(el);
else disconnect();
}
)
return {
width,
height,
};
}
<?php
header("Access-Control-Allow-Origin: *");
header("Content-Type: application/json; charset=UTF-8");
$films = [
[
'id' => 1628,
'name' => 'Sám doma',
'url' => 'https://www.csfd.cz/film/1628-sam-doma/prehled/',
],
[
'id' => 1629,
'name' => 'Sám doma 2',
'url' => 'https://www.csfd.cz/film/1629-sam-doma-2-ztracen-v-new-yorku/prehled/',
]
];
echo json_encode($films);
fetch("https://dog.ceo/api/breeds/image/random/10")
.then((response) => response.json())
.then((data) => data.message)
.then(arrayOfLinks => console.log(arrayOfLinks))
let button = document.querySelector('button')
let span = document.querySelector('span')
let proxy = Alpine.reactive({ color: ‘blue’ })
Alpine.effect(() => {
span.textContent = proxy.color
})
button.addEventListener('click', () => {
proxy.color = (proxy.color == ‘blue’) ? ‘green’ : ‘blue’
})
async function getItems() {
let response = await fetch('/api/items/all')
let json = await response.json()
return json
}
<div x-text="JSON.stringify(getItems())"></div>
// or alternatively:
<div
x-data="{
items: {},
async getItems() {
this.items = await (await fetch('/api/items/all')).json();
}
}"
x-init="getItems">
</div>
// https://github.com/markmead/alpinejs-overlap/blob/main/src/index.js
export default function (Alpine) {
Alpine.magic('overlap', (el, {}) => (targetId) => {
const targetEl = document.querySelector(targetId)
return checkOverlap(
targetEl.getBoundingClientRect(),
el.getBoundingClientRect()
)
})
function checkOverlap(targetBounding, elBounding) {
return !(
targetBounding.top > elBounding.bottom ||
targetBounding.right < elBounding.left ||
targetBounding.bottom < elBounding.top ||
targetBounding.left > elBounding.right
)
}
}
// https://github.com/markmead/alpinejs-slug
import slugify from 'slugify'
export default function (Alpine) {
Alpine.directive('slug', (el, { expression }, { evaluateLater, effect }) => {
let setInputValue = evaluateLater(expression)
effect(() => {
setInputValue((string) => {
el.value = slugify(string, {
lower: true,
})
})
})
})
}
<!-- https://codepen.io/ryangjchandler/pen/qBOEgjg -->
<div x-data="toDoList()" class="max-w-2xl mx-auto px-12 py-8 rounded-lg shadow-lg bg-gray-200">
<div class="flex flex-col items-center justify-center mb-8">
<h1 class="text-3xl font-bold mb-8">
To Do List
</h1>
<input type="text" x-model="newTodo" placeholder="I need to..." class="mx-auto px-4 py-2 rounded shadow text-lg min-w-full" @keydown.enter="addToDo">
</div>
<div class="bg-white w-full rounded shadow mb-8">
<template x-for="(todo, index) in todos" :key="index">
<div class="flex items-center py-4" :class="{ 'border-b border-gray-400': ! isLastToDo(index) }">
<div class="w-1/12 text-center">
<input type="checkbox" @change="toggleToDoCompleted(index)" :checked="todo.completed">
</div>
<div class="w-10/12">
<p x-text="todo.todo" :class="{ 'line-through': todo.completed }"></p>
</div>
<div class="w-1/12 text-center">
<button class="bg-red-600 text-white px-2 py-1 rounded hover:bg-red-700" @click="deleteToDo(index)">
✗
</button>
</div>
</div>
</template>
</div>
<div>
<span x-text="numberOfToDosCompleted()"></span> / <span x-text="toDoCount()"></span> to dos completed
</div>
</div>
<script>
function toDoList() {
return {
newTodo: "",
todos: [],
addToDo() {
this.todos.push({
todo: this.newTodo,
completed: false
});
this.newTodo = "";
},
toggleToDoCompleted(index) {
this.todos[index].completed = !this.todos[index].completed;
},
deleteToDo(index) {
this.todos = this.todos.filter((todo, todoIndex) => {
return index !== todoIndex
})
},
numberOfToDosCompleted() {
return this.todos.filter(todo => todo.completed).length;
},
toDoCount() {
return this.todos.length
},
isLastToDo(index) {
return this.todos.length - 1 === index
}
};
}
</script>
// https://javascript.plainenglish.io/you-dont-need-removeeventlistener-to-remove-dom-event-listeners-12db93cd8bf6
// 1. once
document.querySelector('#btn').addEventListener('click', () => {
console.log('clicked');
}, {once: true});
// 2. AbortController
const controller = new AbortController();
document.querySelector('#btn').addEventListener('click', () => {
console.log('clicked');
}, { signal: controller.signal });
// abort the listener!
controller.abort();
// or
const controller = new AbortController();
const { signal } = controller;
document.querySelector('#btn').addEventListener('click', () => {
console.log('clicked');
}, { signal });
document.querySelector('#btn').addEventListener('mouseenter', () => {
console.log('mouseenter');
}, { signal })
window.addEventListener('scroll', () => {
console.log('scroll');
}, { signal })
// Remove all listeners at once:
controller.abort();
// 3. clone element
const button = document.querySelector('#btn');
button.replaceWith(button.cloneNode(true));