Reply to thread


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

[CODE="javascript"]var $element = $('.someClassName')

if ($element.length)

{

   // you have an object containing one or more elements; you can call various methods which will interact with this element or elements

}[/CODE]


JavaScript / XF 2.3

[CODE="javascript"]const element = document.querySelector('.someClassName')

if (element)

{

    // you have an instance of HTMLElement, if more elements exist with the same selector, you have the first element

}


// ... or


const elements = document.querySelectorAll('someClassName')

if (elements.length)

{

    // you have a NodeList object containing one or more HTMLElement objects

}[/CODE]


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

[CODE="javascript"]var self = this

var callback = function (foo)

{

    self.doSomething(foo)

}[/CODE]


JavaScript / XF 2.3

[CODE="javascript"]const callback = (foo) =>

{

    this.doSomething(foo)

}


// ...or


const callback = (foo) => this.doSomething(foo)[/CODE]

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

[CODE="javascript"]var $element = $('.someClassName')

$element.on('namespace.click', function (e)

{

   e.preventDefault()

    $element.off('namespace.click')

});[/CODE]


JavaScript / XF 2.3

[CODE="javascript"]const element = document.querySelector('.someClassName')

if (element)

{

    XF.on(element, 'namespace.click', e =>

    {

        e.preventDefault()

        XF.off(element, 'namespace.click')

    })

}[/CODE]


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

[CODE="javascript"]var t = this

XF.ajax('some-url', data, callback)

    .always(function ()

    {

        t.loading = false

    })[/CODE]


JavaScript / XF 2.3

[CODE="javascript"]XF.ajax('some-url', data, callback)

    .finally(() =>

    {

        this.loading = false

    })[/CODE]


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

[CODE="javascript"]var $element = $('.someClassName').first() // <span class="someClassName" data-foo="1"></span>


var foo = $element.data('foo') // reads the data-foo attribute from the element in the DOM


var bar = $element.data('bar') // attempts to read the data-bar attribute from the element which doesn't exist, but the internal data store may have a value set


$element.data('bar', [1, 2, 3]) // sets the bar key in the internal data store to an array


$element.data('foo', '100') // the foo entry in the internal data store for this element now returns 100, ignoring the data-foo attribute which remains unchanged in the actual DOM[/CODE]


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

[CODE="javascript"]const element = document.querySelector('.someClassName') // <span class="someClassName" data-foo="1"></span>


const foo = element.dataset.foo // reads the data-foo attribute from the DOM


element.dataset.foo = '100' // sets the data-foo attribute in the DOM


XF.DataStore.set(element, 'bar', [1, 2, 3]) // a new XF.DataStore class is introduced for reading / storing arbitrary, non-string data


const bar = XF.DataStore.get(element, 'bar') // returns an array: [1, 2, 3][/CODE]


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

[CODE="javascript"]var $child = this.$target.find('.someChild').first() // returns the first child element which matches the someChild class[/CODE]


JavaScript / XF 2.3

[CODE="javascript"]const child = this.target.querySelector('.someChild') // returns the first child element which matches the someChild class; this will be a HTMLElement; note it uses the same querySelector method rather than a separately named method[/CODE]


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.


Back
Top Bottom