Although I'm not a fan of jQuery, I find it extremely useful to work with a selector engine.
It is much more convenient and faster to work with $ than with document.querySelectorAll etc.
For this reason I have created a small library to make my work easier.
Ok, I'll show you how I did it:
makeOwnEngine.htm:
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Selector Engine</title>
<link type="text/css" href="makeOwnEngine.css" rel="stylesheet">
<script type="text/javascript" src="makeOwnEngine.js"></script>
</head>
<body>
<h1 id="test">Selector Engine</h1>
<p class="sample-class">Sample Line 1</p>
<p class="sample-class">Sample Line 2</p>
<script>
console.log($('#test'))
</script>
</body>
</html>
makeOwnEngine.css
.bold {
font-weight: bold;
}
.red {
color: red;
}
.blue {
color: blue;
}
.border {
border:1px solid grey;
}
makeOwnEngine.js
"use strict";
const $ = function (selector = null){
class selection {
constructor (selector){
if (selector) {
this.nodes =
(selector === 'document') ? [document] :
document.querySelectorAll(selector)
}
}
}
return selector = new selection(selector)
};
console ouput:
selection {nodes: NodeList(1)}
nodes: NodeList(1)
0: h1#test
length: 1
[[Prototype]]: NodeList
[[Prototype]]: Object
That's not particularly useful yet, we need a little more functionality.
First we have to be able to iterate over the nodelist, we add the following line to makeOwnEngine.js:
each = callback => (this.nodes.forEach( i => callback(i)), this)
Now I would like to assign styles to the elements:
To do this, I add another line to the makeOwnEngine.js:
addClass = classes => this.each(function (i) {
i.classList.add(...classes.split(',')
.map(s => s.trim()))}, this)
the complete makeOwnEngine.js
"use strict";
const $ = function (selector = null){
class selection {
constructor (selector){
if (selector) {
this.nodes =
(selector === 'document') ? [document] :
(selector.nodeType) ? [selector] :
document.querySelectorAll(selector)
}
}
each = callback => (this.nodes.forEach( i => callback(i)), this)
addClass = classes => this.each(function (i) {
i.classList.add(...classes.split(',')
.map(s => s.trim()))}, this)
}
return selector = new selection(selector)
};
Now i would assign style to the elements
h1 #test → class blue and underline
p .sample-class → class red and bold
makeOwnEngine.htm:
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Selector Engine</title>
<link type="text/css" href="makeOwnEngine.css" rel="stylesheet">
<script type="text/javascript" src="makeOwnEngine.js"></script>
</head>
<body>
<h1 id="test">Selector Engine</h1>
<p class="sample-class">Sample Line 1</p>
<p class="sample-class">Sample Line 2</p>
<script>
$('#test')
.addClass('blue')
.addClass('border')
$('.sample-class').addClass('red,bold')
</script>
</body>
</html>
Oh nice, chaining works 😃
This is the basis of my library, which I have expanded to include many more functions:
on = (e,cb) => this.each(i => i.addEventListener(e, cb), this)
// use
$('.sample-class').on('click', ()=>console.log('click'))
// -------------------------
removeClass = c => this.each( e =>
e.classList.remove(...c.split(',')
.map(s => s.trim())),this)
// use
$('#test').removeClass('border')
var $ = (function (sel = null) {
class _{
constructor(sel){
if (sel){
this.els =
(sel === 'document') ? [document] :
(sel === 'window') ? [window] :
(sel.nodeType) ? [sel] :
document.querySelectorAll(sel)
}
}
insSheet = sheet => $(document.head).create('style')
.toNode().sheet.insertRule(sheet)
//functions for Nodes!
addClass = c => this.each(function (i) {
i.classList.add(...c.split(',').map(s => s.trim()))}, this)
addOptions = o => this.each ( i => o.forEach((el,key) => i[key] = new Option(el,el)),this)
addStyle = s => this.each(e => e.setAttribute('style',
(e.getAttribute(`style`)==null)? s : e.getAttribute(`style`)+s), this)
afterHtml = h => this.each( i => i.insertAdjacentHTML('afterend',h),this)
append = (e,n) => $(e).toNode().appendChild(n)
beforeHtml = h => this.each( i => i.insertAdjacentHTML('beforebegin',h),this)
click = cb => this.each(i => i.addEventListener('click', cb),this)
create = t => $(this.els[0].appendChild(document.createElement(t)))
each = cb => (this.els.forEach( i => cb(i)), this)
find = e => $(this.els[0].querySelector(e))
getData = a => this.els[0].getAttribute('data-' + a)
getParent = e => $(this.els[0].parentNode)
getValue = e => this.els[0].value
hasClass = c => this.els[0].classList.contains(c)
hasDataAtt = a => a in this.els[0].dataset
html = h => this.each( i => i.innerHTML = h, this)
inlineStyle = (s,v) => this.each(e => e.style[s]=v, this)
insHtml = h => this.each( i => i.insertAdjacentHTML('beforeEnd',h), this)
last = () => this.els[this.els.length-1]
exists = () => this.els.length
on = (e,cb) => this.each(i => i.addEventListener(e, cb), this)
parentEl = c => $(this.els[0].closest(c))
removeClass = c => this.each( e =>
e.classList.remove(...c.split(',').map(s => s.trim())),this)
removeStyle = () => this.each(e => e.removeAttribute('style'), this)
resetStyles = () => this.each(e => e.setAttribute('style','all:unset;'), this)
setData = (k,v) => (this.els[0].dataset[k]=v, this)
setDataAll = (k,v) => this.each(e => e.dataset[k]=v, this)
style = s => this.each(e => e.setAttribute('style',s), this)
source = s => this.each(e => e.src = s, this)
text = t => this.each( i => i.innerText = t, this)
toggleClass = c => this.each(e => e.classList.toggle(c),this)
toNode = n => this.els[0]
value = v => this.each( i => i.value=v, this)
watch = (callback=null) => {
let handleEvents = evt => handleValues(evt, evt.target)
let fillValues = el => {
if (el.dataset.filter)
el.value = window[el.dataset.filter](el.value)
if (el.dataset.source)
$(`[data-target="${el.dataset.source}"]`).text(el.dataset.func ? window[el.dataset.func](el.value) : el.value)
}
let handleValues = (evt=null, el) => {
switch ((evt == null) ? null : evt.type){
case 'input':
fillValues(el)
break;
case 'click':
if (el.dataset.action) window[el.dataset.action](el)
if (el.dataset.func) window[el.dataset.func](el)
break;
default:
fillValues(el)
}
}
$(`[data-source]`).each(el=>handleValues(null, el));
['input', 'click'].forEach( ev =>
$(this.els[0]).on(ev, e => handleEvents(e)))
if (callback) callback()
return this
}
}
return sel => new _(sel)
})();
const showPic = picUrl =>
$('body').create('div').
inlineStyle('background-color', 'rgba(0,0,0,0.02)').
inlineStyle('width', '100vw').
inlineStyle('height', '100vh').
inlineStyle('position', 'absolute').
inlineStyle('top', '0').
inlineStyle('left', '0').
inlineStyle('display', 'block').
inlineStyle('z-index', '5000').
inlineStyle('backdrop-filter', 'blur(5px)').
inlineStyle('display', 'flex').
inlineStyle('align-items', 'center').
inlineStyle('justify-content', 'center').
on('click', el => $(el.target).toNode().remove()).
create('img').
source(picUrl).
inlineStyle('max-width', '80vw').
inlineStyle('max-height', '80vh').
inlineStyle('box-shadow', 'rgba(0, 0, 0, 0.35) 0px 5px 15px').
on('click', el => $(el.target).
parentEl('div').
toNode().remove()).
parentEl('div').
create('p').
insHtml('✕').
inlineStyle('display', 'block').
inlineStyle('padding', '10px').
inlineStyle('font-size', '20px').
inlineStyle('font-weight', 'bold').
inlineStyle('cursor', 'pointer').
inlineStyle('line-height', '20px').
inlineStyle('margin', '0').
inlineStyle('background-color', '#c00').
inlineStyle('position', 'absolute').
inlineStyle('top', '0').
inlineStyle('left', '0').
on('mouseover', el => $(el.target).inlineStyle('color', 'white')).
on('mouseout', el => $(el.target).inlineStyle('color', 'black')).
on('click', el => $(el.target).
parentEl('div').
toNode().remove()
)