# 🗿 Surreal
### Tiny jQuery alternative for plain Javascript with inline [Locality of Behavior](https://htmx.org/essays/locality-of-behaviour/)!
![cover](https://user-images.githubusercontent.com/24665/171092805-b41286b2-be4a-4aab-9ee6-d604699cc507.png)
(Art by [shahabalizadeh](https://www.deviantart.com/shahabalizadeh))
## Why does this exist?
For devs who love ergonomics! You may appreciate Surreal if:
* You want to stay as close as possible to Vanilla JS.
* Hate typing `document.querySelector` over.. and over..
* Hate typing `addEventListener` over.. and over..
* Really wish `document.querySelectorAll` had Array functions..
* Really wish `this` would work in any inline `
```
See the [Live Example](https://gnat.github.io/surreal/example.html)! Then [view source](https://github.com/gnat/surreal/blob/main/example.html).
## 🎁 Install
Surreal is only 320 lines. No build step. No dependencies.
[📥 Download](https://raw.githubusercontent.com/gnat/surreal/main/surreal.js) into your project, and add `` in your `
`
Or, 🌐 via CDN: ``
## ⚡ Usage
### 🔍️ DOM Selection
* Select **one** element: `me(...)`
* Can be any of:
* CSS selector: `".button"`, `"#header"`, `"h1"`, `"body > .block"`
* Variables: `body`, `e`, `some_element`
* Events: `event.currentTarget` will be used.
* Surreal selectors: `me()`,`any()`
* Choose the start location in the DOM with the 2nd arg. (Default: `document`)
* 🔥 `any('button', me('#header')).classAdd('red')`
* Add `.red` to any `` inside of `#header`
* `me()` ⭐ Get parent element of `
```
```html
I fade out and remove myself.
```
```html
Change color every second.
```
```html
```
#### Array methods
```js
any('button')?.forEach(...)
any('button')?.map(...)
```
## 👁️ Functions
Looking for [DOM Selectors](#selectors)?
Looking for stuff [we recommend doing in vanilla JS](#no-surreal)?
### 🧭 Legend
* 🔗 Chainable off `me()` and `any()`
* 🌐 Global shortcut.
* 🔥 Runnable example.
* 🔌 Built-in Plugin
### 👁️ At a glance
* 🔗 `run`
* It's `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')`
* Leading `.` is **optional**
* Same thing: `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`
* Get: 🔥 `me().attribute('data-x')`
* For single elements.
* For many elements, wrap it in: `any(...).run(...)` or `any(...).forEach(...)`
* Set: 🔥`me().attribute('data-x', true)`
* Set multiple: 🔥 `me().attribute({ 'data-x':'yes', 'data-y':'no' })`
* Remove: 🔥 `me().attribute('data-x', null)`
* Remove multiple: 🔥 `me().attribute({ 'data-x': null, 'data-y':null })`
* 🔗 `send` 🌗 `trigger`
* 🔥 `me().send('change')`
* 🔥 `me().send('change', {'data':'thing'})`
* Wraps `dispatchEvent`
* 🔗 `on`
* 🔥 `me().on('click', ev => { me(ev).styles('background', 'red') })`
* Wraps `addEventListener`
* 🔗 `off`
* 🔥 `me().off('click', fn)`
* Wraps `removeEventListener`
* 🔗 `offAll`
* 🔥 `me().offAll()`
* 🔗 `disable`
* 🔥 `me().disable()`
* Easy alternative to `off()`. Disables click, key, submit events.
* 🔗 `enable`
* 🔥 `me().enable()`
* Opposite of `disable()`
* 🌐 `sleep`
* 🔥 `await sleep(1000, ev => { alert(ev) })`
* `async` version of `setTimeout`
* Wonderful for animation timelines.
* 🌐 `tick`
* 🔥 `await tick()`
* `await` version of `rAF` / `requestAnimationFrame`.
* Animation tick. Waits 1 frame.
* Great if you need to wait for events to propagate.
* 🌐 `rAF`
* 🔥 `rAF(e => { return e })`
* Animation tick. Fires when 1 frame has passed. Alias of [requestAnimationFrame](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame)
* Great if you need to wait for events to propagate.
* 🌐 `rIC`
* 🔥 `rIC(e => { return e })`
* Great time to compute. Fires function when JS is idle. Alias of [requestIdleCallback](https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback)
* 🌐 `halt`
* 🔥 `halt(event)`
* Prevent default browser behaviors.
* Wrapper for [preventDefault](https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
* 🌐 `createElement` 🌗 `create_element`
* 🔥 `e_new = createElement("div"); me().prepend(e_new)`
* Alias of vanilla `document.createElement`
* 🌐 `onloadAdd` 🌗 `onload_add` 🌗 `addOnload` 🌗 `add_onload`
* 🔥 `onloadAdd(_ => { alert("loaded!"); })`
* 🔥 ``
* Execute after the DOM is ready. Similar to jquery `ready()`
* Add to `window.onload` while preventing overwrites of `window.onload` and predictable loading!
* Alternatives:
* Skip missing elements using `?.` example: `me("video")?.requestFullscreen()`
* Place ``
* Inspired by the CSS "next sibling" combinator `+` but in reverse `-`
* Or, use a relative start.
* 🔥 ``
#### Ignore call chain when element is missing.
* 🔥 `me("#i_dont_exist")?.classAdd('active')`
* No warnings: 🔥 `me("#i_dont_exist", document, false)?.classAdd('active')`
## 🔌 Your own plugin
Feel free to edit Surreal directly- but if you prefer, you can use plugins to effortlessly merge with new versions.
```javascript
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")`
* See the included `pluginEffects` for a more comprehensive example.
* Your functions are added globally by `globalsAdd()` If you do not want this, add it to the `restricted` list.
* Refer to an existing function to see how to make yours work with 1 or many elements.
Make an [issue](https://github.com/gnat/surreal/issues) or [pull request](https://github.com/gnat/surreal/pulls) if you think people would like to use it! If it's useful enough we'll want it in core.
### ⭐ Awesome Surreal examples, plugins, and resources: [awesome-surreal](https://github.com/gnat/awesome-surreal) !
## 📚️ Inspired by
* [jQuery](https://jquery.com/) for the chainable syntax we all love.
* [BlingBling.js](https://github.com/argyleink/blingblingjs) for modern minimalism.
* [Bliss.js](https://blissfuljs.com/) for a focus on single elements and extensibility.
* [Hyperscript](https://hyperscript.org) for Locality of Behavior and awesome ergonomics.
* Shout out to [Umbrella](https://umbrellajs.com/), [Cash](https://github.com/fabiospampinato/cash), [Zepto](https://zeptojs.com/)- Not quite as ergonomic. Requires build step to extend.
## 🌘 Future
* Always more `example.html` goodies!
* Automated browser testing perhaps with:
* [Fava](https://github.com/fabiospampinato/fava). See: https://github.com/avajs/ava/issues/24#issuecomment-885949036
* [Ava](https://github.com/avajs/ava/blob/main/docs/recipes/browser-testing.md)
* [jsdom](https://github.com/jsdom/jsdom)
* [jsdom notes](https://github.com/jsdom/jsdom#executing-scripts)