This is a major update with some breaking changes.
up.element
helpers to complement native Element
methods. You might not even miss jQuery anymore.up.util
module now plug the worst emissions in JavaScript's standard library: Equality-by-value, empty-by-value and shallow-copy. Your own objects may hook into those protocols.up.thing.verb()
. up.dom
is now up.fragment
, up.bus
is now up.event
, up.layout
is now up.viewport
.Details below.
jQuery no longer required to use Unpoly. That means Unpoly no longer has any dependencies!
Due to its use of native DOM APIs, Unpoly is now a lot faster. Like, a lot. Ditching jQuery also saves you 30 KB of gzipped bundle size and speeds up your own code.
Effort has been made to ensure that migrating to this version is smooth for existing apps that use jQuery.
All Unpoly functions that accept element arguments will accept both native elements and jQuery collections.
You will need to prefix some function calls with $
to have your callbacks called with jQuery collections instead of native elements:
up.compiler()
callback now receives a native element instead of a jQuery collection. For the old behavior, use up.$compiler()
.up.macro()
callback now received a native element instead of a jQuery collection. For the old behavior, use up.$macro()
.up.on()
now receives an element instead of a jQuery collection. For the old behavior, use up.$on()
.Finally, all Unpoly events (up:*
) are now triggered as native events that can be received with Element#addEventListener()
. You may continue to use jQuery's jQuery#on()
to listen to Unpoly events, but you need to access custom properties through event.originalEvent
.
Also know that if you use jQuery's $.fn.trigger()
to emit events, these events are not received by native event listeners (including Unpoly). Use up.emit()
instead to trigger an event that can be received by both native listeners and jQuery listeners.
See below for detailed changes.
A new, experimental up.element
module offers convience functions for DOM manipulation and traversal.
It complements native Element
methods and works across all supported browsers without polyfills.
up.element.first() |
Returns the first descendant element matching the given selector. |
up.element.all() |
Returns all descendant elements matching the given selector. |
up.element.subtree() |
Returns a list of the given parent's descendants matching the given selector. The list will also include the parent element if it matches the selector itself. |
up.element.closest() |
Returns the first element that matches the selector by testing the element itself and traversing up through its ancestors in the DOM tree. |
up.element.matches() |
Matches all elements that have a descendant matching the given selector. |
up.element.get() |
Casts the given value to a native Element. |
up.element.toggle() |
Display or hide the given element, depending on its current visibility. |
up.element.toggleClass() |
Adds or removes the given class from the given element. |
up.element.hide() |
Hides the given element. |
up.element.show() |
Shows the given element. |
up.element.remove() |
Removes the given element from the DOM tree. |
up.element.replace() |
Replaces the given old element with the given new element. |
up.element.setAttrs() |
Sets all key/values from the given object as attributes on the given element. |
up.element.affix() |
Creates an element matching the given CSS selector and attaches it to the given parent element. |
up.element.createFromSelector() |
Creates an element matching the given CSS selector. |
up.element.createFromHtml() |
Creates an element from the given HTML fragment. |
up.element.toSelector() |
Returns a CSS selector that matches the given element as good as possible. |
up.element.setAttrs() |
Sets all key/values from the given object as attributes on the given element. |
up.element.booleanAttr() |
Returns the value of the given attribute on the given element, cast as a boolean value. |
up.element.numberAttr() |
Returns the value of the given attribute on the given element, cast to a number. |
up.element.jsonAttr() |
Reads the given attribute from the element, parsed as JSON. |
up.element.style() |
Receives computed CSS styles for the given element. |
up.element.styleNumber() |
Receives a computed CSS property value for the given element, casted as a number. |
up.element.setStyle() |
Sets the given CSS properties as inline styles on the given element. |
up.element.isVisible() |
Returns whether the given element is currently visible. |
:has() |
A non-standard pseudo-class that matches all elements that have a descendant matching the given selector. |
up.bus
module has been renamed to up.event
. We want to normalize Unpoly's API to the pattern up.thing.verb()
in the future.up:*
) are now triggered as native events that can be received with Element#addEventListener()
. You may continue to use jQuery's jQuery#on()
to listen to Unpoly events, but you need to access custom properties from event.originalEvent
.event.$target
and event.$element
have been removed from all Unpoly events.
Use the standard event.target
to retrieve the element on which the element was emitted.up.on()
may now bind to a given element by passing it as an (optional) first argument:
up.on(element, '.button', 'click', (event) => { ... })
You may use this for event delegation.
The event handler passed to up.on()
now receives an element instead of a jQuery collection:
up.on('click', (event, element) => {
alert("Clicked on an " + element.tagName)
})
For the old behavior, use up.$on()
.
up.emit()
may now trigger an event on a given element by passing the element as an (optional) first argument:
up.emit(element, 'app:user:login', { email: 'foo@example.com' })
up.emit()
option { message }
is now { log }
.up.emit()
no longer logs by default. You can enable the old efault message with { log: true }
.up.event.nobodyPrevents()
option { message }
is now { log }
.up.reset()
was removed without replacement.up:framework:reset
was removed without replacement.The up.compiler()
callback now receives a native element instead of a jQuery collection:
up.compiler('.button', function(button) {
alert("We have a new button with class " + button.className)
})
For the old behavior, use up.$compiler()
.
The up.macro()
callback now received a native element instead of a jQuery collection:
up.compiler('a.fast-link', function(element) {
element.setAttribute('up-preload', 'up-preload')
element.setAttribute('up-instant', 'up-instant')
})
For the old behavior, use up.$macro()
.
up:form:submit
no longer has a { $form }
property. The event is now emitted
on the form that is being submitted.up.observe()
now accepts a single form field, multiple fields,
a <form>
or any container that contains form fields.
The callback is called once for each change in any of the given elements.The callback for up.observe()
now receives the arguments (value, name)
, where
value
is the changed field value and name
is the [name]
of the field element:
up.observe('form', function(value, name) {
console.log('The value of %o is now %o', name, value);
});
The second argument was previously the observed input element as a jQuery collection.
up.observe()
now accepts a { batch: true }
option to receive all changes
since the last callback in a single object:
up.observe('form', { batch: true }, function(diff) {
console.log('Observed one or more changes: %o', diff);
});
up.form.config.validateTargets
no longer includes the
selector '[up-fieldset]'
.kebab-case
.
camelCase
properties are no longer supported.up.dom
has been renamed to up.fragment
. We want to normalize Unpoly's API to the pattern up.thing.verb()
in the future.up.all()
has been removed without replacementup.first()
has been renamed to up.fragment.first()
to not be confused
with the low-level up.element.first()
.up:fragment:destroy
has been removed without replacement. This event was previously emitted before a fragment was removed. The event up:fragment:destroyed
(emitted after a fragment was removed), remains in the API.up:fragment:destroyed
event no longer has a { $element }
property. It now has a { fragment }
property that contains the detached element. Like before, it is emitted on the former parent of the destroyed element.up:fragment:keep
event have been renamed.up:fragment:kept
event have been renamed.up:fragment:inserted
event have been renamed.up:fragment:destroyed
event have been renamed.The up.util
module now plug the worst emissions in JavaScript's standard library: Equality-by-value, empty-by-value, shallow copy:
up.util.isEqual()
. It returns whether the given arguments are equal by value.up.util.isEqual.key
.
This property contains the name of a method that user-defined classes
may implement to hook into the up.util.isEqual()
protocol.up.util.isBlank()
now returns false for objects with a constructor.up.util.isBlank.key
. This property contains the name of a method that user-defined classes may implement to hook into the up.util.isBlank()
protocol.up.util.copy.key
. This property contains the name of a method that user-defined classes may implement to hook into the up.util.copy()
protocol.More utility functions to have been added to work with lists:
up.util.findResult()
.
It consecutively calls the given function which each element in the given list and
returns the first truthy return value.up.util.flatten()
. This flattens the given list a single level deep.up.util.flatMap()
. This maps each element using a mapping function, then flattens the result into a new array.Some list functions have been renamed to names used in the standard Array
API:
up.util.all()
was renamed to up.util.every()
to match the standard Array#every()
, and to be less confusing with up.element.all()
.up.util.any()
was renamed to up.util.some()
to match the standard
Array#some()
.up.util.select()
was renamed to up.util.filter()
to match the standard
Array#filter()
.up.util.detect()
was renamed to up.util.find()
to match the standard
Array#find()
.All functions that worked for arrays now also work for array-like values:
up.util.isList()
. It returns whether the given argument is an array-like value, like an Array
or a
NodeList
.up.util.reject()
now works for all array-like values, not just arrays.up.util.filter()
now works for all array-like values, not just arrays.up.util.find()
now works for all array-like values, not just arrays.up.util.some()
now works for all array-like values, not just arrays.up.util.every()
now works for all array-like values, not just arrays.And some minor changes:
up.util.nextFrame()
has been renamed to up.util.task()
.up.util.setTimer()
has been renamed to up.util.timer()
.up.util.copy()
now works with Date
objects.up.util.isBoolean()
is now stableup.util.escapeHtml()
is now stableup.util.isJQuery()
now returns false
if no jQuery is loaded into the window.jQuery
globalup.util.unresolvablePromise()
was removed without replacement.up.util.trim()
has been removed without replacement. Use the standard
String#trim()
instead.up.util.parseUrl()
now returns the correct { hostname }
, { protocol }
and { pathname }
properties on IE11.up.util.selectorForElement()
is now up.element.toSelector()
up.layout
module has been renamed to up.viewport
. We want to normalize Unpoly's API to the pattern up.thing.verb()
in the future.up.scroll()
no longer takes a { duration }
or { easing }
option.up.scroll()
now takes a { behavior }
option. Valid values are auto
(no animation) and smooth
(animates the scroll motion).{ behavior: 'smooth' }
by also passing a { speed }
option`.up.viewport.scrollSpeed
. This sets the default speed for smooth scrolling. The default value (1
) roughly corresponds to the default speed of Chrome's native smooth scrolling.up.reveal()
have been changed:
{ duration }
and { easing }
have been removed.{ padding }
to pass the desired padding between the revealed element and the closest viewport edge (in pixels).{ snap }
. It can be true
, false
or a pixel number.{ behavior }
{ speed }
. Defaults to up.viewport.scrollSpeed
.up.layout.config.snap
has been renamed to up.viewport.config.revealSnap
.up.viewport.revealPadding
.up.viewport.root()
. It return the scrolling element
for the browser's main content area.up.viewport.closest()
. It returns the scrolling container for the given element.#hash
anchor is revealed during the initial page load, Unpoly will look for an [up-id=hash]
before looking for [id=hash]
and a[name=hash]
.[up-alias]
now accepts one or more asterisks (*
) anywhere in the pattern.
It was previously limited to match URLs with a given prefix.[up-preload]
and [up-instant]
links no longer bind to the touchstart
event, increasing frame rate while scrolling.The experimental up.params
module has been replaced with the up.Params
class.
Wrap any type of parameter representation into up.Params
to get consistent API for reading
and manipulation.
The following types of parameter representation are supported:
{ email: 'foo@bar.com' }
'email=foo%40bar.com'
{ name, value }
objects like [{ name: 'email', value: 'foo@bar.com' }]
FormData
payloads require a polyfill for FormData#entries()
.Supported methods are:
new up.Params() |
Constructor. |
up.Params#add() |
Adds a new entry with the given name and value . |
up.Params#addAll() |
Adds all entries from the given list of params. |
up.Params#addField() |
Adds params from the given HTML form field. |
up.Params#delete() |
Deletes all entries with the given name . |
up.Params#get() |
Returns the first param value with the given name from the given params . |
up.Params#set() |
Sets the value for the entry with given name . |
up.Params#toArray() |
Returns an array representation of this up.Params instance. |
up.Params#toFormData() |
Returns a FormData representation of this up.Params instance. |
up.Params#toObject() |
Returns an object representation of this up.Params instance. |
up.Params#toQuery() |
Returns an query string for this up.Params instance. |
up.Params#toURL() |
Builds an URL string from the given base URL and this up.Params instance as a query string. |
up.Params.fromFields() |
Constructs a new up.Params instance from one or more HTML form field. |
up.Params.fromForm() |
Constructs a new up.Params instance from the given <form> . |
up.Params.fromURL() |
Constructs a new up.Params instance from the given URL's query string. |
The HTML markup for a popup has been changed to make it easier to style with CSS. The new structure is:
<div class="up-popup">
<div class="up-popup-content">
Fragment content here
</div>
</div>
.up-popup
has been changed. If you have customized popup styles,
you should check if your modifications still work with the new defaults.[up-position]
attribute has been split into two attributes [up-position]
and [up-align]
.
Similarly the { position }
option has been split into two options { position }
and { align }
:
{ position }
defines on which side of the opening element the popup is attached. Valid values are 'top'
, 'right'
, 'bottom'
and 'left'
.{ align }
defines the alignment of the popup along its side.{ position }
is 'top'
or 'bottom'
, valid { align }
values are 'left'
, center'
and 'right'
.{ position }
is 'left'
or 'right'
, valid { align }
values are top'
, center'
and bottom'
.up.popup.sync()
. It forces the popup to update its position when a
layout change is not detected automatically.<body>
.up:popup:open
,up:popup:opened
, up:popup:close
and up:popup:closed
have an { anchor }
property.
It references the element that the popup was attached to.The HTML markup for a popup has been changed to make it easier to style with CSS. The new structure is:
<div class="up-tooltip">
<div class="up-tooltip-content">
Tooltip text here
</div>
</div>
.up-tooltip
have been changed. If you have customized tooltip styles,
you should check if your modifications still work with the new defaults.[up-position]
attribute has been split into two attributes [up-position]
and [up-align]
. Similarly the { position }
option has been split into two options { position }
and { align }
:
{ position }
defines on which side of the opening element the popup is attached. Valid values are 'top'
, 'right'
, 'bottom'
and 'left'
.{ align }
defines the alignment of the popup along its side.{ position }
is 'top'
or 'bottom'
, valid { align }
values are 'left'
, center'
and 'right'
.{ position }
is 'left'
or 'right'
, valid { align }
values are top'
, center'
and bottom'
.up.tooltip.sync()
. It forces the popup to update its position when a
layout change is not detected automatically.<body>
.up:link:preload
event have been renamed.{ overflow-y }
style on the same element
that was chosen by the CSS author (nasty details).
If you're upgrading from an older Unpoly version you should load unpoly-migrate.js
to polyfill deprecated APIs.
Changes handled by unpoly-migrate.js
are not considered breaking changes.
See our upgrading guide for details.