up.compiler(selector, [options], compiler)
JavaScript function
Registers a function to be called when Unpoly inserts a matching element.
When compilers are called
A compiler is called for matching elements:
- on the initial page, when Unpoly boots
- in every new fragment that Unpoly renders
- in elements that you pass to
up.hello()
Elements that other code inserts, e.g. a framework like React, are not compiled automatically. See compiling elements inserted by other code.
Anatomy of a compiler
This code registers a function that is called when an element with a .foo class
is inserted:
up.compiler('.foo', function(element, data, meta) {
// do something with `element`
})
The compiler function is passed three arguments:
- The newly inserted
elementmatching the selector - Any data attached to the element
- Meta information about the current render pass
Destructor functions
When a compiler registers effects outside the compiling element's subtree, it must return a destructor function that reverts the effect. The destructor function is called automatically when the element is swapped or destroyed later.
Examples of non-local effects are global event handlers bound to
the document, or setInterval():
up.compiler('.current-time', function(element) {
let update = () => element.textContent = new Date().toString()
let updateInterval = setInterval(update, 1000)
return () => clearInterval(updateInterval)
})
See cleaning up after yourself for more details and examples.
Batching multiple elements
When multiple elements in an update match the selector, the compiler function is called once for each element.
Let's say a fragment with three .card elements is inserted and compiled:
<div class="fragment">
<div class="card">...</div>
<div class="card">...</div>
<div class="card">...</div>
</div>
A compiler function for .card would be called three separate times:
up.compiler('.card', function(card, data) {
// chip: called three times
})
If you would like the compiler to run once for all matches within
a render pass, use the { batch: true } option:
up.compiler('.card', { batch: true }, function(cards, datas) {
console.debug(`We have ${cards.length} new cards`)
})
Error handling
It is safe to throw exceptions from a compiler or its destructor. A crashing compiler will not interrupt a render pass or prevent other compilers on the same element.
Exceptions thrown by compiler functions are logged to the browser's error console.
Unpoly also emits an error event on window.
See errors in user code for details.
Compilation order
Compilers are called in the order they were registered.
You can control the order by passing a { priority } option.
The default priority is 0. Compilers with a higher priority are run first.
up.compiler('.foo', { priority: 999 }, function() {
// called before compilers with a lower priority
})
When a compiler sets an Unpoly attribute, this usually has no effect since attributes have already been evaluated. Use a macro instead.
See Rendering lifecycle for a full overview of Unpoly's rendering steps.
Asynchronous compilers
Compiler functions can be async.
This is useful when a compiler needs to fetch network resources
or when calling a library with an asynchronous API:
up.compiler('textarea.wysiwyg', async function(textarea) { // mark: async
let editor = await import('wysiwyg-editor') // mark: await
editor.init(textarea)
})
You can also use this to split up expensive tasks, giving the browser a chance to render and process user input:
up.compiler('.element', async function(element) {
doRenderBlockingWork(element)
await scheduler.yield() // mark-line
doUserVisibleWork(element)
})
Cleaning up async work
Like synchronous compilers, async compiler functions can return a destructor function:
up.compiler('textarea.wysiwyg', async function(textarea) {
let editor = await import('wysiwyg-editor')
editor.init(textarea)
return () => editor.destroy(textarea) // mark-line
})
Unpoly guarantees that the destructor is called even if the element is destroyed before the compiler function terminates.
Timing render-blocking mutations
Unpoly will run the first task of every compiler function before allowing the browser to render DOM changes. If an async compiler function runs for multiple tasks, the browser will render between tasks. If you have render-blocking mutations that should be hidden from the user, they must happen in the first task.
After Unpoly updates the DOM, it runs synchronous compilers and the first task of each async compiler.
Only then does the browser render. The promise returned by up.render() fulfills after this first task.
The second task of an async compiler runs after the browser has rendered.
up.render().finished fulfills only after that task is done.
Async compilers will not delay the promise returned by rendering functions up.render() or up.layer.open().
Async compilers will delay the promise returned by up.render().finished
and up.hello().
Registering compilers after booting
When you deliver your JavaScript in multiple files, you may register compilers after Unpoly has booted.
When compilers are registered after Unpoly has booted, they are run on current elements, but only if the compiler has the default priority.
If the compiler has a non-default priority, it is run on future
fragments only. In this case, either remove the { priority } option
or manually call up.hello() on an element that should be
recompiled.
Parameters
The selector to match.
The priority of this compiler.
Compilers with a higher priority are run first. Two compilers with the same priority are run in the order they were registered.
If set to true and a fragment insertion contains multiple
elements matching selector, the compiler function is only called once
with all these elements.
The function to call when an element matching selector is inserted.
Up to three arguments can be accepted:
- The newly inserted
elementmatching the selector - Any data attached to the element
- Meta information about the current render pass
The function may return a destructor function that is called before it is removed from the DOM. The destructor function is called with the compiled element.
The compiler function can be async.