Giter VIP home page Giter VIP logo

nanderoo / surreal Goto Github PK

View Code? Open in Web Editor NEW

This project forked from gnat/surreal

0.0 0.0 0.0 196 KB

๐Ÿ—ฟ Mini jQuery alternative. Dependency-free animations. Pairs with htmx. Locality of Behavior. Use one element or arrays transparently. Vanilla querySelector() but better!

Home Page: https://gnat.github.io/surreal/example.html

License: MIT License

JavaScript 78.49% CSS 6.18% HTML 15.33%

surreal's Introduction

๐Ÿ—ฟ Surreal - tiny jQuery alternative for vanilla JS + inline Locality of Behavior

cover (Art by 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 <script> tag
  • Enjoyed using jQuery selector syntax.
  • Animations, timelines, tweens with no extra libraries.
  • Only 320 lines. No build step. No dependencies.
  • Pairs well with htmx
  • Want fewer layers, less complexity. Are aware of the cargo cult. โœˆ๏ธ

โœจ What does it add to Javascript?

  • โšก๏ธ Locality of Behavior (LoB) Use me() inside <script>
    • Get an element without creating a unique name: No .class or #id needed!
    • this but better!
    • Want me in your CSS <style> tags, too? See our companion script
  • ๐Ÿ”— Call chaining, jQuery style.
  • โ™ป๏ธ Functions work seamlessly on 1 element or arrays of elements!
    • All functions can use: me(), any(), NodeList, HTMLElement (..or arrays of these!)
    • Get 1 element: me()
    • ..or many elements: 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 $())
  • ๐ŸŒ— No forced style. Use: classAdd or class_add or addClass or add_class
    • Use camelCase (Javascript) or snake_case (Python, Rust, PHP, Ruby, SQL, CSS).

๐Ÿค” Why use me() / any() instead of $()

  • ๐Ÿ’ก We solve the classic jQuery code bloat problem: Am I getting 1 element or an array of elements?
    • me() is guaranteed to return 1 element (or first found, or null).
    • any() is guaranteed to return an array (or empty array).
    • No more checks = you write less code. Bonus: Code reads more like self-documenting english.

๐Ÿ‘๏ธ 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).classAdd('.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').trigger('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="/surreal.js"></script> in your <head>

Or, ๐ŸŒ use the CDN: <script src="https://cdn.jsdelivr.net/gh/gnat/surreal/surreal.js"></script>

โšก 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.target will be used.
      • Surreal selectors: me(),any()
      • Adding a start= parameter provides a starting DOM location to select from. Default is document
        • โ–ถ๏ธ any('button', start='header').classAdd('red')
    • me() Get current element for Locality of Behavior in <script> without an explicit .class or #id
    • me("body") Gets <body>
    • me(".button") Gets the first <div class="button">...</div>. To get all of them use any()
  • Select one or more elements as an array: any(...)
    • Similar to me() but guaranteed to return an array (or empty array).
    • any(".foo") Gets all matching elements, such as: <div class="foo">...</div>
    • Feel free to convert between arrays of elements and single elements: any(me()), me(any(".something"))

๐Ÿ”ฅ DOM Functions

  • โ™ป๏ธ All functions work on single elements or arrays of elements.
  • ๐Ÿ”— Start a chain using me() and any()
    • ๐ŸŸข Style A me().classAdd('red') โญ Chain style, recommended!
    • ๐ŸŸ  Style B: classAdd(me(), 'red')
  • ๐ŸŒ Global conveniences help you write less code.
    • globalsAdd() will automatically warn about any clobbering issues.
      • If you prefer no conveniences, just delete globalsAdd()
        • me().classAdd('red') with globalsAdd() removed: $.me().classAdd('red')
        • classAdd(me(), 'red') with globalsAdd() removed: $.classAdd($.me(), 'red')

See: Quick Start and Reference and No Surreal Needed

โšก Quick Start

  • Add a class
    • me().classAdd('red')
    • any("button").classAdd('red')
  • Events
    • me().on("click", ev => me(ev).fadeOut() )
    • on(any('button'), 'click', ev => { me(ev).styles('color: red') })
  • Run functions over elements.
    • any('button').run(_ => { alert(_) })
  • Styles / CSS
    • me().styles('color: red')
    • me().styles({ 'color':'red', 'background':'blue' })
  • Attributes
    • me().attribute('active', true)

Timeline animations without any libraries.

<div>I change color every second.
  <script>
    // Every second animate something new.
    me().on("click", async ev => {
      me(ev).styles({ "transition": "background 1s" })
      await sleep(1000)
      me(ev).styles({ "background": "red" })
      await sleep(1000)
      me(ev).styles({ "background": "green" })
      await sleep(1000)
      me(ev).styles({ "background": "blue" })
      await sleep(1000)
      me(ev).styles({ "background": "none" })
      await sleep(1000)
      me(ev).remove()
    })
  </script>
</div>
<div>I fade out and remove myself.
  <script>me().on("click", ev => { me(ev).fadeOut() })</script>
</div>
<div>I 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

  • ๐Ÿ”— Chainable off me() and any()
  • ๐ŸŒ Global convenience helper.
  • โ–ถ๏ธ 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 for all class functions, and is removed automatically.
      • These are the same: 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')
      • Get is only for single elements. For many, wrap the call 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 })
  • ๐Ÿ”— trigger
    • โ–ถ๏ธ me().trigger('hello')
    • Wraps dispatchEvent
  • ๐Ÿ”— on
    • โ–ถ๏ธ me().on('click', ev => { me(ev).styles('background', 'red') })
    • Wraps addEventListener
  • ๐Ÿ”— off
    • โ–ถ๏ธ me().remove('click')
    • Wraps removeEventListener
  • ๐Ÿ”— offAll
    • โ–ถ๏ธ me().offAll()
  • ๐ŸŒ 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
    • 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
  • ๐ŸŒ halt
    • โ–ถ๏ธ halt(event)
    • Great to prevent default browser behavior: such as displaying an image vs letting JS handle it.
    • Wrapper for 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()
    • Queues functions onto window.onload
    • Why? So you don't overwrite window.onload, also predictable sequential loading!

๐Ÿ”Œ Built-in Plugins

Effects

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

Common effects included:

  • ๐Ÿ”— fadeOut ๐Ÿ” fade_out

    • Fade out and remove element.
    • Keep element with remove=false.
    • โ–ถ๏ธ me().fadeOut()
    • โ–ถ๏ธ me().fadeOut(ev => { alert("Faded out!") }, 3000) Over 3 seconds then call function.
  • ๐Ÿ”— fadeIn ๐Ÿ” fade_in

    • Fade in existing element which has opacity: 0
    • โ–ถ๏ธ me().fadeIn()
    • โ–ถ๏ธ me().fadeIn(ev => { alert("Faded in!") }, 3000) Over 3 seconds then call function.

๐Ÿ”ฎ No Surreal Needed

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

Logging

  • ๐ŸŒ console.log() console.warn() console.error()
  • Event logging: โ–ถ๏ธ monitorEvents(me()) See: Chrome Blog

Benchmarking / Time It!

  • โ–ถ๏ธ console.time('name')
  • โ–ถ๏ธ console.timeEnd('name')

Text / HTML Content

  • โ–ถ๏ธ me().textContent = "hello world"
    • XSS Safe! See: MDN
  • โ–ถ๏ธ 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)

๐Ÿ’Ž Conventions & Tips

  • _ = for temporary or unused variables. Keep it short and sweet!
  • e, el, elt = element
  • e, ev, evt = event
  • f, fn = function
  • Many things can be done in vanilla HTML / CSS (ex: dropdowns).
  • Find where your change touches the least code.
  • Simplicity and ergonomics tend to have exponential payoff.

๐Ÿ”Œ Adding a function

Feel free to modify Surreal for a particular project any way you like- but you can use this system to effortlessly merge functions with new versions.

  1. Add your function
var $thing = {
  test(e, name) {
    console.log(`Hello ${name} from ${e}`)
    return e
  }
}
$ = {...$, ...$thing}
  1. Is your function chainable? Add to sugar()
$.sugars['test'] = (name) => { return $.test($._e, name) }
  1. Your function will be added globally by globalsAdd() If you do not want this (ex: name clash), add it to the restricted list.

Refer to an existing function to see how to make your function work with 1 or many elements.

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

  • jQuery for the chainable syntax we all love.
  • BlingBling.js for modern minimalism.
  • Bliss.js for a focus on single elements and extensibility.
  • Hyperscript for Locality of Behavior and awesome ergonomics.
  • Shout out to Umbrella, Cash, Zepto- Not quite as ergonomic. Requires build step to extend.

๐ŸŒ˜ Future

surreal's People

Contributors

gnat avatar sieroslawski avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.