Surreal JavaScript Library

repository·main·Indexed 23 days ago

https://github.com/gnat/surreal

A tiny, dependency-free JavaScript library designed as a modern alternative to jQuery. Surreal emphasizes 'Locality of Behavior' (LoB) via the me() function, allowing developers to write self-contained scripts within HTML elements. It provides a suite of chainable DOM manipulation functions, animation helpers like fadeOut and fadeIn, and a plugin system for custom extensions.

Tokens
2.2K
Snippets
6
Records
9
Agent score
33%

What's inside Surreal

  1. Handle null safety and missing elements

    main

    When selecting elements that might not exist, use optional chaining (?.) to prevent errors.

    • Optional Chaining: me("#i_dont_exist")?.classAdd('active')
    • Silent Warnings: To avoid console warnings when an element is missing, pass document and false as additional arguments: me("#i_dont_exist", document, false)?.classAdd('active').
  2. How `me()` and `any()` work for DOM selection

    main

    Surreal uses two primary selection functions to solve the ambiguity of whether a selector returns a single element or an array.

    • me(...): Guaranteed to return one element (the first found, or null).
      • Calling me() without arguments returns the parent element of the current <script> tag. This enables Locality of Behavior (LoB), allowing you to write scripts that act on their containing element without needing unique IDs or classes.
      • Supports CSS selectors (e.g., me('.button')), variables, and event objects.
      • Accepts an optional second parameter to define the starting DOM node (defaults to document).
    • any(...): Guaranteed to return an array of elements (or an empty array).
      • Use this when you want to perform operations on all matching elements.
      • Works seamlessly with standard Array methods like .forEach(), .map(), and .filter().

    You can convert between them using any(me()) or me(any(...)).

  3. Install Surreal

    main

    Surreal is a lightweight (320 lines) jQuery alternative for plain JavaScript. You can install it by downloading surreal.js directly into your project or using a CDN.

    Direct Download: Download surreal.js and include it in your <head>:

    <script src="/surreal.js"></script>

    CDN: Include the script via jsDelivr:

    <script src="https://cdn.jsdelivr.net/gh/gnat/surreal@main/surreal.js"></script>
    <script src="https://cdn.jsdelivr.net/gh/gnat/surreal@main/surreal.js"></script>
  4. Extend Surreal with custom plugins

    main

    You can add your own functionality to Surreal by pushing a function into the surreal.plugins array. To make your functions chainable, ensure they return the element e.

    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)
    
    // Usage
    me().hello("Internet")
  5. Use Surreal DOM functions and chaining

    main

    Surreal functions are designed to work identically on single elements or arrays of elements. You can use them via method chaining (recommended) or as global functions.

    Chaining Style (Recommended):

    me().classAdd('red')

    Global Function Style:

    classAdd(me(), 'red')

    Note on Global Conveniences: By default, Surreal provides global conveniences (like classAdd()) that are attached to the window. If you prefer to avoid global namespace pollution, you can delete globalsAdd() from the source. To use the namespaced version, use surreal.me() or surreal.classAdd().

    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', 'background':'blue' })
    me().attribute('active', true)
  6. Perform animations and timelines with Surreal

    main

    Surreal includes built-in animation helpers like fadeOut() and supports manual timelines using async/await and sleep() without external libraries.

    Example: Fade out on click

    <div>
      <script>me().on("click", ev => { me(ev).fadeOut() })</script>
    </div>

    Example: Manual Timeline

    <div>
      <script>
        me().on("click", async ev => {
          let el = me(ev) // Capture element to avoid losing context in async
          me(el).styles({ "transition": "background 1s" })
          await sleep(1000)
          me(el).styles({ "background": "red" })
          // ... continue sequence
        })
      </script>
    </div>
    <div>
      <script>
        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>
  7. Select void elements and siblings

    main

    To select elements like <input type="text" /> that are followed by a <script> tag, use the following selectors:

    • me('-') or me('prev'): Selects the previous sibling (inspired by the CSS - combinator).
    • Relative start: Use a unique attribute on the target element to select it relative to a parent: me('[n1]', me()) where n1 is an attribute on the target.
    <!-- Example using me('-') to select the input from the script -->
    <input type="text" /> 
    <script>me('-').value = "hello"</script>
    
    <!-- Example using relative attribute selection -->
    <form> 
      <input type="text" n1 /> 
      <script>me('[n1]', me()).value = "hello"</script> 
    </form>
  8. Use built-in effect plugins: fadeOut and fadeIn

    main

    Surreal includes built-in plugins for common visual effects. These can be combined with CSS transitions.

    • fadeOut(callback, duration): Fades out the element and removes it. To keep the element after fading, set remove=false (implied context). You can provide a callback and duration in milliseconds.
    • fadeIn(callback, duration): Fades in an existing element that has opacity: 0.
  9. Reference the Surreal function API

    main

    Surreal provides a suite of chainable and global functions for DOM manipulation.

    Chainable functions (via me() or any()):

    • run(fn): Executes a function on the element(s). Works on single elements or collections.
    • remove(): Removes the element(s).
    • classAdd(name): Adds a class (leading . is optional).
    • classRemove(name): Removes a class.
    • classToggle(name): Toggles a class.
    • styles(cssStringOrObject): Sets styles. Use an object with null values to remove styles.
    • attribute(name, value): Gets or sets attributes. Use an object to set/remove multiple.
    • send(type, data): Wraps dispatchEvent to trigger events.
    • on(type, fn): Wraps addEventListener.
    • off(type, fn): Wraps removeEventListener.
    • offAll(): Removes all event listeners.
    • disable(): Disables click, key, and submit events.
    • enable(): Re-enables events.

    Global functions:

    • createElement(tagName): Alias of document.createElement.
    • sleep(ms, callback): Async version of setTimeout.
    • halt(event): Stops propagation and prevents default actions.
    • tick(): Async version of requestAnimationFrame (waits 1 frame).
    • rAF(fn): Alias of requestAnimationFrame.
    • rIC(fn): Alias of requestIdleCallback.
    • onloadAdd(fn): Executes code after the DOM is ready (similar to jQuery ready()).
    // Examples of chainable API
    me().run(e => { alert(e) })
    any('button').remove()
    me().classAdd('active')
    me().styles({ 'color':'red', 'background':'blue' })
    me().attribute({ 'data-x':'yes', 'data-y':'no' })
    me().on('click', ev => { me(ev).styles('background', 'red') })
    
    // Examples of global API
    await sleep(1000, ev => { alert(ev) })
    await tick()