gnat / surreal

πŸ—Ώ Mini jQuery alternative. Dependency-free animations. Locality of Behavior. Use one element or arrays transparently. Pairs with htmx. Vanilla querySelector() but better!
https://gnat.github.io/surreal/example.html
MIT License
1.2k stars 24 forks source link
animation cash dom dom-manipulation ergonomics htmx hyperscript javascript jquery jquery-alternative jquery-like jquery-replacement locality-of-behavior queryselector queryselectorall selector surreal timeline umbrella vanilla-js

πŸ—Ώ Surreal

Tiny jQuery alternative for plain Javascript with inline Locality of Behavior!

cover (Art by shahabalizadeh)

Why does this exist?

For devs who love ergonomics! You may appreciate Surreal if:

✨ What does it add to Javascript?

πŸ€” Why use me() / any() instead of $()

πŸ‘οΈ How does it look?

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.

🎁 Install

Surreal is only 320 lines. No build step. No dependencies.

πŸ“₯ Download into your project, and add <script src="https://github.com/gnat/surreal/raw/main/surreal.js"></script> in your <head>

Or, 🌐 via CDN: <script src="https://cdn.jsdelivr.net/gh/gnat/surreal@main/surreal.js"></script>

⚑ Usage

πŸ”οΈ DOM Selection

πŸ”₯ DOM Functions

See: Quick Start and Reference and No Surreal Needed

⚑ Quick Start

Timeline animations without any libraries.

<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>

Array methods

any('button')?.forEach(...)
any('button')?.map(...)

πŸ‘οΈ Functions

Looking for DOM Selectors? Looking for stuff we recommend doing in vanilla JS?

🧭 Legend

πŸ”Œ Built-in Plugins

Effects

Build effects with me().styles({...}) with timelines using CSS transitioned await or callbacks.

Common effects included:

βšͺ No Surreal Needed

More often than not, Vanilla JS is the easiest way!

Logging

Benchmarking / Time It!

Text / HTML Content

Children

Append / Prepend elements.

AJAX (replace jQuery ajax())

Scope functions and variables inside <script>

Select a void element like <input type="text" />

Ignore call chain when element is missing.

πŸ”Œ Your own plugin

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")

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.

⭐ Awesome Surreal examples, plugins, and resources: awesome-surreal !

πŸ“šοΈ Inspired by

🌘 Future