Bye bye jQuery
After being part of our product since the beginning, it is now the right time to say goodbye to jQuery.
jQuery is a JavaScript library which encapsulates a great deal of native JavaScript functionality into an alternative set of functions with a liberal sprinkling of syntactic sugar.
JavaScript and browser standards have evolved significantly over the history of XenForo. At one point it would have been unfathomable to use anything else. In the not too distant past, jQuery was practically essential for even being able to find a specific element with a specific class name, or being able to support the seemingly endless quirks in now-ancient versions of Internet Explorer and others.
It's a sizeable library in itself too weighing in at over 30 KB added to every page load, and how much of that library were we actually using, anyway?
Well, as the developer who personally went through and rewrote nearly 40,000 lines of code, a lot less than you'd think. And of the language features of jQuery we were using, many things are a simple straight swap to a native JavaScript function that, a long time ago, may either have not existed at all, or too new to have garnered enough browser support.
We acknowledge that there will be some pain points for existing developers who have existing code based on jQuery but, in truth, as long as you aren't maintaining anywhere near the 40,000 lines of code we are, it should be a relatively smooth transition. But, if you get completely stuck, you could always re-add jQuery if you wish but, we'd recommend avoiding that if you can. And removing jQuery as a dependency can start now if you're planning on making changes to existing code before XenForo 2.3 is released. We strongly advise against writing new code that directly uses jQuery functionality at this point.
If needed, we can go into a little bit more technical detail down the road about the changes we have made, but here are some highlights.
Note: The next section gets rather into the weeds in terms of development specifics so move on to the next section if this doesn't interest you.
Block scoped variables
While not strictly related to jQuery, it's worth noting that we no longer use
var
to define variables in favour of using
let
and
const
. This makes the scoping of variables clearer and code less error prone and more predictable.
Selecting element(s)
Selecting elements from the DOM is probably the most frequent operation you'll perform in JavaScript and therefore this is a significant change which, while slightly more verbose, makes code much clearer and less error prone.
jQuery / XF 2.2
JavaScript:
var $element = $('.someClassName')
if ($element.length)
{
}
JavaScript / XF 2.3
JavaScript:
const element = document.querySelector('.someClassName')
if (element)
{
}
const elements = document.querySelectorAll('someClassName')
if (elements.length)
{
}
Arrow functions
Again, while not jQuery related, you will now see
arrow functions being utilised as much as possible. As well as being syntactically nicer to use than traditional anonymous functions, they do not create new bindings for keywords such as
this
.
jQuery / XF 2.2
JavaScript:
var self = this
var callback = function (foo)
{
self.doSomething(foo)
}
JavaScript / XF 2.3
JavaScript:
const callback = (foo) =>
{
this.doSomething(foo)
}
const callback = (foo) => this.doSomething(foo)
Event handling
Some functionality provided by jQuery was difficult to leave behind, and the majority of those
really useful methods have been rewritten and ported to vanilla JavaScript as additional methods to our
XF
object. Not least of these is jQuery's event management which supports namespaced events and provides a more intuitive way of removing event listeners from an element that doesn't require a reference to the original event callback.
jQuery / XF 2.2
JavaScript:
var $element = $('.someClassName')
$element.on('namespace.click', function (e)
{
e.preventDefault()
$element.off('namespace.click')
});
JavaScript / XF 2.3
JavaScript:
const element = document.querySelector('.someClassName')
if (element)
{
XF.on(element, 'namespace.click', e =>
{
e.preventDefault()
XF.off(element, 'namespace.click')
})
}
AJAX
This is mostly unchanged from XenForo 2.2 because we still have a
XF.ajax()
wrapper to use as a helper method but, behind the scenes, rather than using jQuery's
$.ajax()
method (which is a wrapper around
XMLHttpRequest
) we have migrated over to using the more modern,
Fetch API.
The main thing to be aware of here is that the
Promise methods available from the result of calling
XF.ajax()
are named slightly differently to what they were with jQuery.
jQuery / XF 2.2
JavaScript:
var t = this
XF.ajax('some-url', data, callback)
.always(function ()
{
t.loading = false
})
JavaScript / XF 2.3
JavaScript:
XF.ajax('some-url', data, callback)
.finally(() =>
{
this.loading = false
})
Storing arbitrary data for an element
Some of jQuery's features, while powerful, can sometimes appear inconsistent or ambiguous. One such feature is the
data
method available on jQuery objects. Depending on its usage, it can manifest different behaviors. Consider the following example:
jQuery / XF 2.2
JavaScript:
var $element = $('.someClassName').first()
var foo = $element.data('foo')
var bar = $element.data('bar')
$element.data('bar', [1, 2, 3])
$element.data('foo', '100')
In XenForo, there remains a necessity to store arbitrary data, especially data that isn't always a string. However, our current approaches are more predictable and consistent:
JavaScript / XF 2.3
JavaScript:
const element = document.querySelector('.someClassName')
const foo = element.dataset.foo
element.dataset.foo = '100'
XF.DataStore.set(element, 'bar', [1, 2, 3])
const bar = XF.DataStore.get(element, 'bar')
Handler targets
We have a special variable which we pass in to all element/event handlers which is
this.$target
currently. Note that this becomes
this.target
in XF 2.3 as conventionally the
$
prefix on variable names typically is used to denote a jQuery object. In XF 2.3
this.target
represents a
HTMLElement
object.
To find children of the target, this is a little more consistent with vanilla JavaScript than it was with jQuery:
jQuery / XF 2.2
JavaScript:
var $child = this.$target.find('.someChild').first()
JavaScript / XF 2.3
JavaScript:
const child = this.target.querySelector('.someChild')
Migration support
While understandably, some of the custom methods in XF 2.3 will be unavailable to you until release, we would encourage you to start migrating as much code as possible now to use vanilla JavaScript where practical.
If you feel you need support with converting code to vanilla JavaScript we have approximately 40,000 lines of experience between us and we will attempt to reply to queries in the
XenForo development discussions forum where we can.