home to your local SPACEGIRL 馃挮 arimelody.space
at dev 96 lines 3.4 kB view raw
1/** 2 * Creates a reorderable list from any `container` and viable list item selector. 3 * 4 * This function is absolute magic and I love it 5 * 6 * Example: 7 * ```html 8 * <ul id="list"> 9 * <li>Item 1</li> 10 * <li>Item 2</li> 11 * <li>Item 3</li> 12 * </ul> 13 * ``` 14 * ```js 15 * // javascript 16 * makeMagicList(document.getElementById("list"), "li"); 17 * ``` 18 * 19 * @param {HTMLElement} container The parent container to use as a list. 20 * @param {string} itemSelector The selector name of list item elements. 21 * @param {Function} callback A function to call after each reordering. 22 */ 23export function makeMagicList(container, itemSelector, callback) { 24 if (!container) 25 throw new Error("container not provided"); 26 if (!itemSelector) 27 throw new Error("itemSelector not provided"); 28 29 container.querySelectorAll(itemSelector).forEach(item => { 30 item.draggable = true; 31 item.addEventListener("dragstart", () => { item.classList.add("moving") }); 32 item.addEventListener("dragend", () => { item.classList.remove("moving") }); 33 34 // dragging on inputs should take priority 35 item.querySelectorAll("input").forEach(el => { 36 el.addEventListener("mousedown", () => { item.draggable = false }); 37 el.addEventListener("mouseup", () => { item.draggable = true }); 38 el.addEventListener("dragstart", e => { e.stopPropagation() }); 39 }); 40 }); 41 42 var lastCursorY; 43 container.addEventListener("dragover", event => { 44 const dragging = container.querySelector(itemSelector + ".moving"); 45 if (!dragging) return; 46 47 let cursorY = event.touches ? event.touches[0].clientY : event.clientY; 48 49 // don't bother processing if we haven't moved 50 if (lastCursorY === cursorY) return 51 lastCursorY = cursorY; 52 53 // get the element positioned ahead of the cursor 54 const notMoving = [...container.querySelectorAll(itemSelector + ":not(.moving)")]; 55 const afterElement = notMoving.reduce((previous, current) => { 56 const box = current.getBoundingClientRect(); 57 const offset = cursorY - box.top - box.height / 2; 58 if (offset < 0 && offset > previous.offset) 59 return { offset: offset, element: current }; 60 return previous; 61 }, { offset: Number.NEGATIVE_INFINITY }).element; 62 63 if (afterElement) { 64 container.insertBefore(dragging, afterElement); 65 } else { 66 container.appendChild(dragging); 67 } 68 69 if (callback) callback(); 70 }); 71} 72 73export function hijackClickEvent(container, link) { 74 container.addEventListener('click', event => { 75 if (event.target.tagName.toLowerCase() === 'a') return; 76 event.preventDefault(); 77 link.dispatchEvent(new MouseEvent('click', { 78 bubbles: true, 79 cancelable: true, 80 view: window, 81 ctrlKey: event.ctrlKey, 82 metaKey: event.metaKey, 83 shiftKey: event.shiftKey, 84 altKey: event.altKey, 85 button: event.button, 86 })); 87 }); 88} 89 90document.addEventListener("readystatechange", () => { 91 const navbar = document.getElementById("navbar"); 92 document.getElementById("toggle-nav").addEventListener("click", event => { 93 event.preventDefault(); 94 navbar.classList.toggle("open"); 95 }) 96});