{"id":13515022,"url":"https://github.com/gnat/surreal","last_synced_at":"2025-05-15T11:09:07.759Z","repository":{"id":37990640,"uuid":"499786894","full_name":"gnat/surreal","owner":"gnat","description":"🗿 Mini jQuery alternative. Dependency-free animations. Locality of Behavior.  Use one element or arrays transparently. Pairs with htmx. Vanilla querySelector() but better!","archived":false,"fork":false,"pushed_at":"2024-10-09T00:21:12.000Z","size":308,"stargazers_count":1525,"open_issues_count":7,"forks_count":29,"subscribers_count":18,"default_branch":"main","last_synced_at":"2025-05-15T01:43:41.293Z","etag":null,"topics":["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"],"latest_commit_sha":null,"homepage":"https://gnat.github.io/surreal/example.html","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gnat.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-06-04T09:57:26.000Z","updated_at":"2025-05-14T18:19:01.000Z","dependencies_parsed_at":"2023-09-26T20:02:23.408Z","dependency_job_id":"72ce4653-7a53-4193-90a7-d36c6918c23f","html_url":"https://github.com/gnat/surreal","commit_stats":null,"previous_names":[],"tags_count":6,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gnat%2Fsurreal","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gnat%2Fsurreal/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gnat%2Fsurreal/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gnat%2Fsurreal/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gnat","download_url":"https://codeload.github.com/gnat/surreal/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254328386,"owners_count":22052632,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["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"],"created_at":"2024-08-01T05:01:05.439Z","updated_at":"2025-05-15T11:09:07.737Z","avatar_url":"https://github.com/gnat.png","language":"JavaScript","readme":"# 🗿 Surreal\n### Tiny jQuery alternative for plain Javascript with inline [Locality of Behavior](https://htmx.org/essays/locality-of-behaviour/)!\n\n![cover](https://user-images.githubusercontent.com/24665/171092805-b41286b2-be4a-4aab-9ee6-d604699cc507.png)\n(Art by [shahabalizadeh](https://www.deviantart.com/shahabalizadeh))\n\u003c!--\n\u003ca href=\"https://github.com/gnat/surreal/archive/refs/heads/main.zip\"\u003e\u003cimg src=\"https://img.shields.io/badge/Download%20.zip-ff9800?style=for-the-badge\u0026color=%234400e5\" alt=\"Download badge\" /\u003e\u003c/a\u003e\n\n\u003ca href=\"https://github.com/gnat/surreal\"\u003e\u003cimg src=\"https://img.shields.io/github/workflow/status/gnat/surreal/ci?label=ci\u0026style=for-the-badge\u0026color=%237d91ce\" alt=\"CI build badge\" /\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/gnat/surreal/releases\"\u003e\u003cimg src=\"https://img.shields.io/github/workflow/status/gnat/surreal/release?label=Mini\u0026style=for-the-badge\u0026color=%237d91ce\" alt=\"Mini build badge\" /\u003e\u003c/a\u003e\n\u003ca href=\"https://github.com/gnat/surreal/blob/main/LICENSE\"\u003e\u003cimg src=\"https://img.shields.io/github/license/gnat/surreal?style=for-the-badge\u0026color=%234400e5\" alt=\"License badge\" /\u003e\u003c/a\u003e--\u003e\n\n## Why does this exist?\n\nFor devs who love ergonomics! You may appreciate Surreal if:\n\n* You want to stay as close as possible to Vanilla JS.\n* Hate typing `document.querySelector` over.. and over..\n* Hate typing `addEventListener` over.. and over..\n* Really wish `document.querySelectorAll` had Array functions..\n* Really wish `this` would work in any inline `\u003cscript\u003e` tag\n* Enjoyed using jQuery selector syntax.\n* [Animations, timelines, tweens](#-quick-start) with no extra libraries.\n* Only 320 lines. No build step. No dependencies.\n* Pairs well with [htmx](https://htmx.org)\n* Want fewer layers, less complexity. Are aware of the cargo cult. ✈️\n\n## ✨ What does it add to Javascript?\n\n* ⚡️ [Locality of Behavior (LoB)](https://htmx.org/essays/locality-of-behaviour/) Use `me()` inside `\u003cscript\u003e`\n  * No **.class** or **#id** needed! Get an element without creating a unique name.\n  * `this` but much more flexible!\n  * Want `me` in your CSS `\u003cstyle\u003e` tags, too? See our [companion script](https://github.com/gnat/css-scope-inline)\n* 🔗 Call chaining, jQuery style.\n* ♻️ Functions work seamlessly on 1 element or arrays of elements!\n  * All functions can use: `me()`, `any()`, `NodeList`, `HTMLElement` (..or arrays of these!)\n  * Get 1 element: `me()`\n  * ..or many elements: `any()`\n  * `me()` or `any()` can chain with any Surreal function.\n    * `me()` can be used directly as a single element (like `querySelector()` or `$()`)\n    * `any()` can use: `for` / `forEach` / `filter` / `map` (like `querySelectorAll()` or `$()`)\n* 🌗 No forced style. Use: `classAdd` or `class_add` or `addClass` or `add_class`\n  * Use `camelCase` (Javascript) or `snake_case` (Python, Rust, PHP, Ruby, SQL, CSS).\n\n### 🤔 Why use `me()` / `any()` instead of `$()`\n* 💡 Solves the classic jQuery bloat problem: Am I getting 1 element or an array of elements?\n  * `me()` is guaranteed to return 1 element (or first found, or null).\n  * `any()` is guaranteed to return an array (or empty array).\n  * No more checks = write less code. Bonus: Reads more like self-documenting english.\n\n## 👁️ How does it look?\n\nDo surreal things with [Locality of Behavior](https://htmx.org/essays/locality-of-behaviour/) like:\n```html\n\u003clabel for=\"file-input\" \u003e\n  \u003cdiv class=\"uploader\"\u003e\u003c/div\u003e\n  \u003cscript\u003e\n    me().on(\"dragover\", ev =\u003e { halt(ev); me(ev).classAdd('.hover'); console.log(\"Files in drop zone.\") })\n    me().on(\"dragleave\", ev =\u003e { halt(ev); me(ev).classRemove('.hover'); console.log(\"Files left drop zone.\") })\n    me().on(\"drop\", ev =\u003e { halt(ev); me(ev).classRemove('.hover').classAdd('.loading'); me('#file-input').attribute('files', ev.dataTransfer.files); me('#form').send('change') })\n  \u003c/script\u003e\n\u003c/label\u003e\n```\n\nSee the [Live Example](https://gnat.github.io/surreal/example.html)! Then [view source](https://github.com/gnat/surreal/blob/main/example.html).\n\n## 🎁 Install\n\nSurreal is only 320 lines. No build step. No dependencies.\n\n[📥 Download](https://raw.githubusercontent.com/gnat/surreal/main/surreal.js) into your project, and add `\u003cscript src=\"/surreal.js\"\u003e\u003c/script\u003e` in your `\u003chead\u003e`\n\nOr, 🌐 via CDN: `\u003cscript src=\"https://cdn.jsdelivr.net/gh/gnat/surreal@main/surreal.js\"\u003e\u003c/script\u003e`\n\n## ⚡ Usage\n\n### \u003ca name=\"selectors\"\u003e\u003c/a\u003e🔍️ DOM Selection\n\n* Select **one** element: `me(...)`\n  * Can be any of:\n    * CSS selector: `\".button\"`, `\"#header\"`, `\"h1\"`, `\"body \u003e .block\"`\n    * Variables: `body`, `e`, `some_element`\n    * Events: `event.currentTarget` will be used.\n    * Surreal selectors: `me()`,`any()`\n    * Choose the start location in the DOM with the 2nd arg. (Default: `document`)\n      * 🔥 `any('button', me('#header')).classAdd('red')`\n        * Add `.red` to any `\u003cbutton\u003e` inside of `#header`\n  * `me()` ⭐ Get parent element of `\u003cscript\u003e` without a **.class** or **#id** !\n  * `me(\"body\")` Gets `\u003cbody\u003e`\n  * `me(\".button\")` Gets the first `\u003cdiv class=\"button\"\u003e...\u003c/div\u003e`. To get all of them use `any()`\n* Select **one or more** elements as an array: `any(...)`\n  * Like `me()` but guaranteed to return an array (or empty array). \n  * `any(\".foo\")` ⭐ Get all matching elements.\n  * Convert between arrays of elements and single elements: `any(me())`, `me(any(\".something\"))`\n \n### 🔥 DOM Functions\n\n* ♻️ All functions work on single elements or arrays of elements.\n* 🔗 Start a chain using `me()` and `any()`\n  * 🟢 Style A `me().classAdd('red')` ⭐ Chain style. Recommended!\n  * 🟠 Style B: `classAdd(me(), 'red')`\n* 🌐 Global conveniences help you write less code.\n  * `globalsAdd()` will automatically warn you of any clobbering issues!\n  * 💀🩸 If you want no conveniences, or are a masochist, delete `globalsAdd()`\n    * 🟢 `me().classAdd('red')` becomes `surreal.me().classAdd('red')`\n    * 🟠 `classAdd(me(), 'red')` becomes `surreal.classAdd(surreal.me(), 'red')`\n\nSee: [Quick Start](#quick-start) and [Reference](#reference) and [No Surreal Needed](#no-surreal)\n\n## \u003ca name=\"quick-start\"\u003e\u003c/a\u003e⚡ Quick Start\n\n* Add a class\n  * `me().classAdd('red')`\n  * `any(\"button\").classAdd('red')`\n* Events\n  * `me().on(\"click\", ev =\u003e me(ev).fadeOut() )`\n  * `any('button').on('click', ev =\u003e { me(ev).styles('color: red') })`\n* Run functions over elements.\n  * `any('button').run(_ =\u003e { alert(_) })`\n* Styles / CSS\n  * `me().styles('color: red')`\n  * `me().styles({ 'color':'red', 'background':'blue' })`\n* Attributes\n  * `me().attribute('active', true)`\n\n\u003ca name=\"timelines\"\u003e\u003c/a\u003e\n#### Timeline animations without any libraries.\n```html\n\u003cdiv\u003eI change color every second.\n  \u003cscript\u003e\n    // On click, animate something new every second.\n    me().on(\"click\", async ev =\u003e {\n      let el = me(ev) // Save target because async will lose it.\n      me(el).styles({ \"transition\": \"background 1s\" })\n      await sleep(1000)\n      me(el).styles({ \"background\": \"red\" })\n      await sleep(1000)\n      me(el).styles({ \"background\": \"green\" })\n      await sleep(1000)\n      me(el).styles({ \"background\": \"blue\" })\n      await sleep(1000)\n      me(el).styles({ \"background\": \"none\" })\n      await sleep(1000)\n      me(el).remove()\n    })\n  \u003c/script\u003e\n\u003c/div\u003e\n```\n```html\n\u003cdiv\u003eI fade out and remove myself.\n  \u003cscript\u003eme().on(\"click\", ev =\u003e { me(ev).fadeOut() })\u003c/script\u003e\n\u003c/div\u003e\n```\n```html\n\u003cdiv\u003eChange color every second.\n  \u003cscript\u003e\n    // Run immediately.\n    (async (e = me()) =\u003e {\n      me(e).styles({ \"transition\": \"background 1s\" })\n      await sleep(1000)\n      me(e).styles({ \"background\": \"red\" })\n      await sleep(1000)\n      me(e).styles({ \"background\": \"green\" })\n      await sleep(1000)\n      me(e).styles({ \"background\": \"blue\" })\n      await sleep(1000)\n      me(e).styles({ \"background\": \"none\" })\n      await sleep(1000)\n      me(e).remove()\n    })()\n  \u003c/script\u003e\n\u003c/div\u003e\n```\n```html\n\u003cscript\u003e\n  // Run immediately, for every \u003cbutton\u003e globally!\n  (async () =\u003e {\n    any(\"button\").fadeOut()\n  })()\n\u003c/script\u003e\n```\n#### Array methods\n```js\nany('button')?.forEach(...)\nany('button')?.map(...)\n```\n\n## \u003ca name=\"reference\"\u003e\u003c/a\u003e👁️ Functions\nLooking for [DOM Selectors](#selectors)?\nLooking for stuff [we recommend doing in vanilla JS](#no-surreal)?\n### 🧭 Legend\n* 🔗 Chainable off `me()` and `any()`\n* 🌐 Global shortcut.\n* 🔥 Runnable example.\n* 🔌 Built-in Plugin\n### 👁️ At a glance\n\n* 🔗 `run`\n  * It's `forEach` but less wordy and works on single elements, too!\n  * 🔥 `me().run(e =\u003e { alert(e) })`\n  * 🔥 `any('button').run(e =\u003e { alert(e) })`\n* 🔗 `remove`\n  * 🔥 `me().remove()`\n  * 🔥 `any('button').remove()`\n* 🔗 `classAdd` 🌗 `class_add` 🌗 `addClass` 🌗 `add_class`\n  * 🔥 `me().classAdd('active')`\n  * Leading `.` is **optional**\n    * Same thing: `me().classAdd('active')` 🌗 `me().classAdd('.active')`\n* 🔗 `classRemove` 🌗 `class_remove` 🌗 `removeClass` 🌗 `remove_class`\n  * 🔥 `me().classRemove('active')`\n* 🔗 `classToggle` 🌗 `class_toggle` 🌗 `toggleClass` 🌗 `toggle_class`\n  * 🔥 `me().classToggle('active')`\n* 🔗 `styles`\n  * 🔥 `me().styles('color: red')` Add style.\n  * 🔥 `me().styles({ 'color':'red', 'background':'blue' })` Add multiple styles.\n  * 🔥 `me().styles({ 'background':null })` Remove style.\n* 🔗 `attribute` 🌗 `attributes` 🌗 `attr`\n  * Get: 🔥 `me().attribute('data-x')`\n    * For single elements.\n    * For many elements, wrap it in: `any(...).run(...)` or `any(...).forEach(...)`\n  * Set: 🔥`me().attribute('data-x', true)`\n  * Set multiple: 🔥 `me().attribute({ 'data-x':'yes', 'data-y':'no' })`\n  * Remove: 🔥 `me().attribute('data-x', null)`\n  * Remove multiple: 🔥 `me().attribute({ 'data-x': null, 'data-y':null })`\n* 🔗 `send` 🌗 `trigger`\n  * 🔥 `me().send('change')`\n  * 🔥 `me().send('change', {'data':'thing'})`\n  * Wraps `dispatchEvent`\n* 🔗 `on`\n  * 🔥 `me().on('click', ev =\u003e { me(ev).styles('background', 'red') })`\n  * Wraps `addEventListener`\n* 🔗 `off`\n  * 🔥 `me().off('click', fn)`\n  * Wraps `removeEventListener`\n* 🔗 `offAll`\n  * 🔥 `me().offAll()`\n* 🔗 `disable`\n  * 🔥 `me().disable()`\n  * Easy alternative to `off()`. Disables click, key, submit events.\n* 🔗 `enable`\n  * 🔥 `me().enable()`\n  * Opposite of `disable()`\n* 🌐 `createElement` 🌗 `create_element`\n  * 🔥 `e_new = createElement(\"div\"); me().prepend(e_new)`\n  * Alias of [document.createElement](https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement)\n* 🌐 `sleep`\n  * 🔥 `await sleep(1000, ev =\u003e { alert(ev) })`\n  * `async` version of `setTimeout`\n  * Wonderful for animation timelines.\n* 🌐 `halt`\n  * 🔥 `halt(event)`\n  * When recieving an event, stop propagation, and prevent default actions (such as form submit).\n  * Wrapper for [stopPropagation](https://developer.mozilla.org/en-US/docs/Web/API/Event/stopPropagation) and [preventDefault](https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)\n* 🌐 `tick`\n  * 🔥 `await tick()`\n  * `await` version of `rAF` / `requestAnimationFrame`.\n  * Waits for 1 frame (browser paint).\n  * Useful to guarantee CSS properties are applied, and events have propagated.\n* 🌐 `rAF`\n  * 🔥 `rAF(e =\u003e { return e })`\n  * Calls after 1 frame (browser paint). Alias of [requestAnimationFrame](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame)\n  * Useful to guarantee CSS properties are applied, and events have propagated.\n* 🌐 `rIC`\n  * 🔥 `rIC(e =\u003e { return e })`\n  * Calls when Javascript is idle. Alias of [requestIdleCallback](https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback)\n* 🌐 `onloadAdd` 🌗 `onload_add` 🌗 `addOnload` 🌗 `add_onload`\n  * 🔥 `onloadAdd(_ =\u003e { alert(\"loaded!\"); })`\n  * 🔥 `\u003cscript\u003elet e = me(); onloadAdd(_ =\u003e { me(e).on(\"click\", ev =\u003e { alert(\"clicked\") }) })\u003c/script\u003e`\n  * Execute after the DOM is ready. Similar to jquery `ready()`\n  * Add to `window.onload` while preventing overwrites of `window.onload` and predictable loading!\n  * Alternatives:\n    * Skip missing elements using `?.` example: `me(\"video\")?.requestFullscreen()`\n    * Place `\u003cscript\u003e` after the loaded element.\n      * See `me('-')` / `me('prev')`\n* 🔌 `fadeOut`\n  * See below\n* 🔌 `fadeIn`\n  * See below\n\n### \u003ca name=\"plugin-included\"\u003e\u003c/a\u003e🔌 Built-in Plugins\n\n### Effects\nBuild effects with `me().styles({...})` with timelines using [CSS transitioned `await` or callbacks](#timelines).\n\nCommon effects included:\n\n* 🔗 `fadeOut` 🌗 `fade_out`\n  * Fade out and remove element.\n  * Keep element with `remove=false`.\n  * 🔥 `me().fadeOut()`\n  * 🔥 `me().fadeOut(ev =\u003e { alert(\"Faded out!\") }, 3000)` Over 3 seconds then call function.\n\n* 🔗 `fadeIn` 🌗 `fade_in`\n  * Fade in existing element which has `opacity: 0`\n  * 🔥 `me().fadeIn()`\n  * 🔥 `me().fadeIn(ev =\u003e { alert(\"Faded in!\") }, 3000)` Over 3 seconds then call function.\n\n\n## \u003ca name=\"no-surreal\"\u003e\u003c/a\u003e⚪ No Surreal Needed\n\nMore often than not, Vanilla JS is the easiest way!\n\nLogging\n* 🔥 `console.log()` `console.warn()` `console.error()`\n* Event logging: 🔥 `monitorEvents(me())` See: [Chrome Blog](https://developer.chrome.com/blog/quickly-monitor-events-from-the-console-panel-2/)\n\nBenchmarking / Time It!\n* 🔥 `console.time('name')`\n* 🔥 `console.timeEnd('name')`\n\nText / HTML Content\n* 🔥 `me().textContent = \"hello world\"`\n  * XSS Safe! See: [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent)\n* 🔥 `me().innerHTML = \"\u003cp\u003ehello world\u003c/p\u003e\"`\n* 🔥 `me().innerText = \"hello world\"`\n\nChildren\n* 🔥 `me().children`\n* 🔥 `me().children.hidden = true`\n\nAppend / Prepend elements.\n* 🔥 `me().prepend(new_element)`\n* 🔥 `me().appendChild(new_element)`\n* 🔥 `me().insertBefore(element, other_element.firstChild)`\n* 🔥 `me().insertAdjacentHTML(\"beforebegin\", new_element)`\n\nAJAX (replace jQuery `ajax()`)\n* Use [htmx](https://htmx.org/) or [htmz](https://leanrada.com/htmz/) or [fetch()](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) or [XMLHttpRequest()](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest)\n* Example using `fetch()`\n```js\nme().on(\"click\", async event =\u003e {\n  let e = me(event)\n  // EXAMPLE 1: Hit an endpoint.\n  if((await fetch(\"/webhook\")).ok) console.log(\"Did the thing.\")\n  // EXAMPLE 2: Get content and replace me()\n  try {\n    let response = await fetch('/endpoint')\n    if (response.ok) e.innerHTML = await response.text()\n    else console.warn('fetch(): Bad response')\n  }\n  catch (error) { console.warn(`fetch(): ${error}`) }\n})\n```\n* Example using `XMLHttpRequest()`\n```js\nme().on(\"click\", async event =\u003e {\n  let e = me(event)\n  // EXAMPLE 1: Hit an endpoint.\n  var xhr = new XMLHttpRequest()\n  xhr.open(\"GET\", \"/webhook\")\n  xhr.send()\n  // EXAMPLE 2: Get content and replace me()\n  var xhr = new XMLHttpRequest()\n  xhr.open(\"GET\", \"/endpoint\")\n  xhr.onreadystatechange = () =\u003e {\n    if (xhr.readyState == 4 \u0026\u0026 xhr.status \u003e= 200 \u0026\u0026 xhr.status \u003c 300) e.innerHTML = xhr.responseText\n  }\n  xhr.send()\n})\n```\n\n ## 💎 Conventions \u0026 Tips\n\n* Many ideas can be done in HTML / CSS (ex: dropdowns)\n* `_` = for temporary or unused variables. Keep it short and sweet!\n* `e`, `el`, `elt` = element\n* `e`, `ev`, `evt` = event\n* `f`, `fn` = function\n\n#### Scope functions and variables inside `\u003cscript\u003e`\n  * ⭐ Use a block `{ let note = \"hi\"; function hey(text) { alert(text) }; me().on('click', ev =\u003e { hey(note) }) }`\n    * `let` and `function` is scoped within `{ }`\n  * ⭐ Use `me()`\n    *  `me().hey = (text) =\u003e { alert(text) }`\n    *  `me().on('click', (ev) =\u003e { me(ev).hey(\"hi\") })`\n  * ⭐ Use an event `me().on('click', ev =\u003e { /* add and call function here */ })`\n  * Use an inline module: `\u003cscript type=\"module\"\u003e`\n    * Note: `me()` in modules will not see `parentElement`, explicit selectors are required: `me(\".mybutton\")`\n\n#### Select a void element like `\u003cinput type=\"text\" /\u003e`\n* Use: `me('-')` or `me('prev')` or `me('previous')`\n  * 🔥 `\u003cinput type=\"text\" /\u003e \u003cscript\u003eme('-').value = \"hello\"\u003c/script\u003e`\n  * Inspired by the CSS \"next sibling\" combinator `+` but in reverse `-`\n* Or, use a relative start.\n  * 🔥 `\u003cform\u003e \u003cinput type=\"text\" n1 /\u003e \u003cscript\u003eme('[n1]', me()).value = \"hello\"\u003c/script\u003e \u003c/form\u003e`\n\n#### Ignore call chain when element is missing.\n* 🔥 `me(\"#i_dont_exist\")?.classAdd('active')`\n* No warnings: 🔥 `me(\"#i_dont_exist\", document, false)?.classAdd('active')`\n\n## \u003ca name=\"plugins\"\u003e\u003c/a\u003e🔌 Your own plugin\n\nFeel free to edit Surreal directly- but if you prefer, you can use plugins to effortlessly merge with new versions.\n\n```javascript\nfunction pluginHello(e) {\n  function hello(e, name=\"World\") {\n    console.log(`Hello ${name} from ${e}`)\n    return e // Make chainable.\n  }\n  // Add sugar\n  e.hello = (name) =\u003e { return hello(e, name) }\n}\n\nsurreal.plugins.push(pluginHello)\n```\n\nNow use your function like: `me().hello(\"Internet\")`\n\n* See the included `pluginEffects` for a more comprehensive example.\n* Your functions are added globally by `globalsAdd()` If you do not want this, add it to the `restricted` list.\n* Refer to an existing function to see how to make yours work with 1 or many elements.\n\nMake 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.\n\n### ⭐ Awesome Surreal examples, plugins, and resources: [awesome-surreal](https://github.com/gnat/awesome-surreal) !\n\n## 📚️ Inspired by\n\n* [jQuery](https://jquery.com/) for the chainable syntax we all love.\n* [BlingBling.js](https://github.com/argyleink/blingblingjs) for modern minimalism.\n* [Bliss.js](https://blissfuljs.com/) for a focus on single elements and extensibility.\n* [Hyperscript](https://hyperscript.org) for Locality of Behavior and awesome ergonomics.\n* 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.\n\n## 🌘 Future\n* Always more `example.html` goodies!\n* Automated browser testing perhaps with:\n  * [Fava](https://github.com/fabiospampinato/fava). See: https://github.com/avajs/ava/issues/24#issuecomment-885949036\n  * [Ava](https://github.com/avajs/ava/blob/main/docs/recipes/browser-testing.md)\n  * [jsdom](https://github.com/jsdom/jsdom)\n    * [jsdom notes](https://github.com/jsdom/jsdom#executing-scripts)\n","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgnat%2Fsurreal","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgnat%2Fsurreal","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgnat%2Fsurreal/lists"}