(Art by shahabalizadeh) <!β
For devs who love ergonomics! You may appreciate Surreal if:
document.querySelector
over.. and over..addEventListener
over.. and over..document.querySelectorAll
had Array functions..this
would work in any inline <script>
tagme()
inside <script>
this
but much more flexible!me
in your CSS <style>
tags, too? See our companion scriptme()
, any()
, NodeList
, HTMLElement
(..or arrays of these!)me()
any()
me()
or any()
can chain with any Surreal function.
me()
can be used directly as a single element (like querySelector()
or $()
)any()
can use: for
/ forEach
/ filter
/ map
(like querySelectorAll()
or $()
)classAdd
or class_add
or addClass
or add_class
camelCase
(Javascript) or snake_case
(Python, Rust, PHP, Ruby, SQL, CSS).me()
/ any()
instead of $()
me()
is guaranteed to return 1 element (or first found, or null).any()
is guaranteed to return an array (or empty array).Do surreal things with Locality of Behavior like:
<label for="file-input" >
<div class="uploader"></div>
<script>
me().on("dragover", ev => { halt(ev); me(ev).classAdd('.hover'); console.log("Files in drop zone.") })
me().on("dragleave", ev => { halt(ev); me(ev).classRemove('.hover'); console.log("Files left drop zone.") })
me().on("drop", ev => { halt(ev); me(ev).classRemove('.hover').classAdd('.loading'); me('#file-input').attribute('files', ev.dataTransfer.files); me('#form').send('change') })
</script>
</label>
See the Live Example! Then view source.
Surreal is only 320 lines. No build step. No dependencies.
π₯ Download into your project, and add <script src="/surreal.js"></script>
in your <head>
Or, π via CDN: <script src="https://cdn.jsdelivr.net/gh/gnat/surreal@main/surreal.js"></script>
me(...)
".button"
, "#header"
, "h1"
, "body > .block"
body
, e
, some_element
event.currentTarget
will be used.me()
,any()
document
)
any('button', me('#header')).classAdd('red')
.red
to any <button>
inside of #header
me()
β Get parent element of <script>
without a .class or #id !me("body")
Gets <body>
me(".button")
Gets the first <div class="button">...</div>
. To get all of them use any()
any(...)
me()
but guaranteed to return an array (or empty array).any(".foo")
β Get all matching elements.any(me())
, me(any(".something"))
me()
and any()
me().classAdd('red')
β Chain style. Recommended!classAdd(me(), 'red')
globalsAdd()
will automatically warn you of any clobbering issues!globalsAdd()
me().classAdd('red')
becomes surreal.me().classAdd('red')
classAdd(me(), 'red')
becomes surreal.classAdd(surreal.me(), 'red')
See: Quick Start and Reference and No Surreal Needed
me().classAdd('red')
any("button").classAdd('red')
me().on("click", ev => me(ev).fadeOut() )
any('button').on('click', ev => { me(ev).styles('color: red') })
any('button').run(_ => { alert(_) })
me().styles('color: red')
me().styles({ 'color':'red', 'background':'blue' })
me().attribute('active', true)
<div>I change color every second.
<script>
// On click, animate something new every second.
me().on("click", async ev => {
let el = me(ev) // Save target because async will lose it.
me(el).styles({ "transition": "background 1s" })
await sleep(1000)
me(el).styles({ "background": "red" })
await sleep(1000)
me(el).styles({ "background": "green" })
await sleep(1000)
me(el).styles({ "background": "blue" })
await sleep(1000)
me(el).styles({ "background": "none" })
await sleep(1000)
me(el).remove()
})
</script>
</div>
<div>I fade out and remove myself.
<script>me().on("click", ev => { me(ev).fadeOut() })</script>
</div>
<div>Change color every second.
<script>
// Run immediately.
(async (e = me()) => {
me(e).styles({ "transition": "background 1s" })
await sleep(1000)
me(e).styles({ "background": "red" })
await sleep(1000)
me(e).styles({ "background": "green" })
await sleep(1000)
me(e).styles({ "background": "blue" })
await sleep(1000)
me(e).styles({ "background": "none" })
await sleep(1000)
me(e).remove()
})()
</script>
</div>
<script>
// Run immediately, for every <button> globally!
(async () => {
any("button").fadeOut()
})()
</script>
any('button')?.forEach(...)
any('button')?.map(...)
Looking for DOM Selectors? Looking for stuff we recommend doing in vanilla JS?
me()
and any()
run
forEach
but less wordy and works on single elements, too!me().run(e => { alert(e) })
any('button').run(e => { alert(e) })
remove
me().remove()
any('button').remove()
classAdd
π class_add
π addClass
π add_class
me().classAdd('active')
.
is optional
me().classAdd('active')
π me().classAdd('.active')
classRemove
π class_remove
π removeClass
π remove_class
me().classRemove('active')
classToggle
π class_toggle
π toggleClass
π toggle_class
me().classToggle('active')
styles
me().styles('color: red')
Add style.me().styles({ 'color':'red', 'background':'blue' })
Add multiple styles.me().styles({ 'background':null })
Remove style.attribute
π attributes
π attr
me().attribute('data-x')
any(...).run(...)
or any(...).forEach(...)
me().attribute('data-x', true)
me().attribute({ 'data-x':'yes', 'data-y':'no' })
me().attribute('data-x', null)
me().attribute({ 'data-x': null, 'data-y':null })
send
π trigger
me().send('change')
me().send('change', {'data':'thing'})
dispatchEvent
on
me().on('click', ev => { me(ev).styles('background', 'red') })
addEventListener
off
me().off('click', fn)
removeEventListener
offAll
me().offAll()
disable
me().disable()
off()
. Disables click, key, submit events.enable
me().enable()
disable()
createElement
π create_element
e_new = createElement("div"); me().prepend(e_new)
sleep
await sleep(1000, ev => { alert(ev) })
async
version of setTimeout
halt
halt(event)
tick
await tick()
await
version of rAF
/ requestAnimationFrame
.rAF
rAF(e => { return e })
rIC
rIC(e => { return e })
onloadAdd
π onload_add
π addOnload
π add_onload
onloadAdd(_ => { alert("loaded!"); })
<script>let e = me(); onloadAdd(_ => { me(e).on("click", ev => { alert("clicked") }) })</script>
ready()
window.onload
while preventing overwrites of window.onload
and predictable loading!?.
example: me("video")?.requestFullscreen()
<script>
after the loaded element.
me('-')
/ me('prev')
fadeOut
fadeIn
Build effects with me().styles({...})
with timelines using CSS transitioned await
or callbacks.
Common effects included:
fadeOut
π fade_out
remove=false
.me().fadeOut()
me().fadeOut(ev => { alert("Faded out!") }, 3000)
Over 3 seconds then call function.fadeIn
π fade_in
opacity: 0
me().fadeIn()
me().fadeIn(ev => { alert("Faded in!") }, 3000)
Over 3 seconds then call function.More often than not, Vanilla JS is the easiest way!
Logging
console.log()
console.warn()
console.error()
monitorEvents(me())
See: Chrome BlogBenchmarking / Time It!
console.time('name')
console.timeEnd('name')
Text / HTML Content
me().textContent = "hello world"
me().innerHTML = "<p>hello world</p>"
me().innerText = "hello world"
Children
me().children
me().children.hidden = true
Append / Prepend elements.
me().prepend(new_element)
me().appendChild(new_element)
me().insertBefore(element, other_element.firstChild)
me().insertAdjacentHTML("beforebegin", new_element)
AJAX (replace jQuery ajax()
)
fetch()
me().on("click", async event => {
let e = me(event)
// EXAMPLE 1: Hit an endpoint.
if((await fetch("/webhook")).ok) console.log("Did the thing.")
// EXAMPLE 2: Get content and replace me()
try {
let response = await fetch('/endpoint')
if (response.ok) e.innerHTML = await response.text()
else console.warn('fetch(): Bad response')
}
catch (error) { console.warn(`fetch(): ${error}`) }
})
XMLHttpRequest()
me().on("click", async event => {
let e = me(event)
// EXAMPLE 1: Hit an endpoint.
var xhr = new XMLHttpRequest()
xhr.open("GET", "/webhook")
xhr.send()
// EXAMPLE 2: Get content and replace me()
var xhr = new XMLHttpRequest()
xhr.open("GET", "/endpoint")
xhr.onreadystatechange = () => {
if (xhr.readyState == 4 && xhr.status >= 200 && xhr.status < 300) e.innerHTML = xhr.responseText
}
xhr.send()
})
## π Conventions & Tips
_
= for temporary or unused variables. Keep it short and sweet!e
, el
, elt
= elemente
, ev
, evt
= eventf
, fn
= function<script>
{ let note = "hi"; function hey(text) { alert(text) }; me().on('click', ev => { hey(note) }) }
let
and function
is scoped within { }
me()
me().hey = (text) => { alert(text) }
me().on('click', (ev) => { me(ev).hey("hi") })
me().on('click', ev => { /* add and call function here */ })
<script type="module">
me()
in modules will not see parentElement
, explicit selectors are required: me(".mybutton")
<input type="text" />
me('-')
or me('prev')
or me('previous')
<input type="text" /> <script>me('-').value = "hello"</script>
+
but in reverse -
<form> <input type="text" n1 /> <script>me('[n1]', me()).value = "hello"</script> </form>
me("#i_dont_exist")?.classAdd('active')
me("#i_dont_exist", document, false)?.classAdd('active')
Feel free to edit Surreal directly- but if you prefer, you can use plugins to effortlessly merge with new versions.
function pluginHello(e) {
function hello(e, name="World") {
console.log(`Hello ${name} from ${e}`)
return e // Make chainable.
}
// Add sugar
e.hello = (name) => { return hello(e, name) }
}
surreal.plugins.push(pluginHello)
Now use your function like: me().hello("Internet")
pluginEffects
for a more comprehensive example.globalsAdd()
If you do not want this, add it to the restricted
list.Make an issue or pull request if you think people would like to use it! If itβs useful enough weβll want it in core.