Skip to content

Vanilla JavaScript

UntilJS works in any JavaScript environment without frameworks.

Basic Usage

javascript
import until from 'untiljs'

let value = 0
setTimeout(() => { value = 5 }, 1000)

await until(() => value).toBe(5)
console.log('Value is now 5!')

DOM Elements

Wait for DOM element to appear:

javascript
import until from 'untiljs'

// Wait for element to exist
await until(() => document.getElementById('my-element')).toBeTruthy()

const element = document.getElementById('my-element')
console.log('Element found:', element)

Custom Events

Wait for custom events:

javascript
import until from 'untiljs'

let eventFired = false

document.addEventListener('my-custom-event', () => {
  eventFired = true
})

// Trigger event after 1 second
setTimeout(() => {
  document.dispatchEvent(new CustomEvent('my-custom-event'))
}, 1000)

await until(() => eventFired).toBe(true)
console.log('Event fired!')

User Interactions

Wait for user actions:

javascript
import until from 'untiljs'

let buttonClicked = false

document.getElementById('my-button').addEventListener('click', () => {
  buttonClicked = true
})

await until(() => buttonClicked).toBe(true, { timeout: 30000 })
console.log('Button was clicked!')

Async Data Loading

javascript
import until from 'untiljs'

let data = null

fetch('/api/data')
  .then(res => res.json())
  .then(json => { data = json })

await until(() => data).toBeTruthy()
console.log('Data loaded:', data)

Web Workers

javascript
import until from 'untiljs'

const worker = new Worker('worker.js')
let result = null

worker.onmessage = (e) => {
  result = e.data
}

worker.postMessage({ task: 'process' })

await until(() => result).toBeTruthy()
console.log('Worker result:', result)
worker.terminate()

Animation Completion

javascript
import until from 'untiljs'

const element = document.getElementById('animated')
let animationComplete = false

element.addEventListener('animationend', () => {
  animationComplete = true
})

element.classList.add('animate')

await until(() => animationComplete).toBe(true)
console.log('Animation complete!')

Intersection Observer

javascript
import until from 'untiljs'

const element = document.getElementById('observe-me')
let isVisible = false

const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      isVisible = true
    }
  })
})

observer.observe(element)

await until(() => isVisible).toBe(true)
console.log('Element is visible!')
observer.disconnect()

CDN Usage

Use directly in HTML without a build step:

html
<!DOCTYPE html>
<html>
<head>
  <title>UntilJS Example</title>
</head>
<body>
  <div id="result">Loading...</div>

  <script src="https://unpkg.com/untiljs@latest/dist/index.iife.min.js"></script>
  <script>
    async function main() {
      let value = 0

      setTimeout(() => {
        value = 5
        document.getElementById('result').textContent = 'Value is now 5!'
      }, 1000)

      await until(() => value).toBe(5)
      console.log('Done!')
    }

    main()
  </script>
</body>
</html>

Creating Subscribable Objects

For better performance in vanilla JS, create subscribable objects:

javascript
import until, { createStore } from 'untiljs'

// Using createStore
const store = createStore(0)

// Subscribe to changes
store.subscribe(value => {
  console.log('Value changed:', value)
})

// Update value
store.value = 5

// Wait for value
await until(store).toBe(5)

// Manual subscribable
const manualStore = {
  _value: 0,
  _listeners: new Set(),

  get value() {
    return this._value
  },

  set value(newValue) {
    this._value = newValue
    this._listeners.forEach(l => l(newValue))
  },

  subscribe(callback) {
    this._listeners.add(callback)
    callback(this._value)
    return () => this._listeners.delete(callback)
  }
}

Released under the MIT License.