Saw your project, very nice, and played with the code for a bit, I shuffled some things around and made it smaller and so there's no use of "this" (for users' convenience and preventing binding issues). Instead of checking for arrays, you can just assume a value is array like and go with it, this makes the code a bit smaller/simpler and could make it a little faster since you're not checking and converting (e.g. [...selectAll()]
). Just some thoughts Good job with femto, all the best!
const func = (() => {
const argToElements = src => {
if (src != null) {
if (src.isFemtoJS) return src.sel()
if (typeof src === 'string') {
const tagName = /^<(\w+)>$/.exec(src)
return tagName != null
? [document.createElement(tagName[1])]
: document.querySelectorAll(src)
}
if (src instanceof Element) return [src]
if (src.length) {
const elems = []
for (let i = 0; i < src.length; i++) elems.push(...argToElements(src[i]))
return elems
}
}
throw new TypeError('Expected string | HTMLElement | Array | femtoJS, instead got ' + typeof src)
}
const $ = function () {
const sel = argToElements(arguments)
const iter = fn => {
for (let i = 0; i < sel.length; i++) fn(sel[i], i)
return O
}
const insertToAdjacent = s => e => iter((j, i) => i === 0 ?
e instanceof Element ?
e.insertAdjacentElement(s, j) :
e.sel()[0].insertAdjacentElement(s, j) :
sel[0].insertAdjacentElement('afterend', j))
const insertAdjacent = s => sOrE => {
if (typeof sOrE !== 'string') {
if (sOrE instanceof Element) {
sel[0].insertAdjacentElement(s, sOrE)
} else if (sOrE.isFemtoJS) {
const osel = sOrE.sel()
sel[0].insertAdjacentElement(s, osel[0])
for (let i = 1; i < osel.length; i++) {
osel[0].insertAdjacentElement('afterend', osel[i])
}
}
} else {
sel[0].insertAdjacentHTML(s, sOrE)
}
return O
}
const O = {
on: (type, fn) => iter(i => i.addEventListener(type, fn)),
off: (type, fn) => iter(i => i.removeEventListener(type, fn)),
css: s => iter(i => i.style.cssText += s),
html: h => iter(i => i.innerHTML = h),
text: t => iter(i => i.innerText = t),
addClass: t => iter(i => i.classList.add(t)),
toggleClass: t => iter(i => i.classList.toggle(t)),
removeClass: t => iter(i => i.classList.remove(t)),
empty: () => iter(i => i.innerHTML = ''),
attr: (k, v) => iter(i => i.setAttribute(k, v)),
removeAttr: k => iter(i => i.removeAttribute(k)),
parent: () => iter((e, i) => { sel[i] = e.parentNode }),
remove: () => iter(i => i.remove()),
before: insertAdjacent('beforebegin'),
after: insertAdjacent('afterend'),
first: insertAdjacent('afterbegin'),
last: insertAdjacent('beforeend'),
insertBefore: insertToAdjacent('beforebegin'),
insertAfter: insertToAdjacent('afterend'),
insertFirst: insertToAdjacent('afterbegin'),
insertLast: insertToAdjacent('beforeend'),
prepend: insertAdjacent('afterbegin'),
append: insertAdjacent('beforeend'),
getAttr: v => sel[0].getAttribute(v),
offset: () => sel[0].getBoundingClientRect(),
sel: () => sel,
isFemtoJS: true
}
return O
}
$.fragment = () => $(document.createDocumentFragment())
return $
})
if (typeof module === 'object' && module.exports) {
module.exports = func()
} else if (typeof define === 'function' && define.amd) {
const singleton = func()
define('femtoJS', [], () => singleton)
} else {
window.$ = func()
}