How to replace jQuery with D3 in a project
- Transfer
When creating visualizations or interactive pages, we often use a combination of jQuery and D3. Moreover, D3 is mainly used, and from jQuery they take a small set of functions for manipulating the DOM.
And although there are powerful features in D3 - selectors and ajax wrappers, we often miss some functions from jQuery. We will show how jQuery can be replaced using D3 everywhere. As a result, your code will be simplified, the volume of the project will decrease, and you will not mix different approaches, but will use the functions as is customary in D3.
First, let's look at how these two libraries converge. This is convenient for those who already know jQuery and want to learn D3.
Both libraries are based on easy-to-use, but feature-rich selectors.
jQuery
D3
jQuery
D3
The syntax is slightly different, but both D3 and jQuery have good wrappers for
ajax.
jQuery
D3
It is often necessary to manage DOM element classes, for example, to switch styles.
jQuery
D3
Insertion of child nodes is an important function, especially when visualizing input data. This is easy and simple:
jQuery
D3
The same syntax is designed to track events on selected elements.
jQuery
D3
Sometimes you need to remove items from the DOM. Here's how to do it:
jQuery
D3
You can select children from a larger
jQuery selection
D3
You can use the following functions to modify the contents of a DOM node.
jQuery
D3
Now consider the features that are in jQuery but not in D3. For each of them, there is a simple solution to replace it, as well as a more general use case, which can be useful to you anywhere in your application, using branded D3 chains.
One of the advantages of jQuery is the convenience of working with events. You can trigger or track custom events for any item on the page. For example, you can start a custom event with some data for your document, and track it in code:
In D3, this is not directly supported, but you can always achieve this behavior. A simple option (if you do not need d3.event in the handler):
A more general approach is to add a function to the d3 object so that it can be used on any selection.
Adding this function to D3 allows you to get a trigger function that resembles that of jQuery, which can be used as follows:
d3.select (document) .trigger ('dataChange', {newData: 'HelloWorld!'});
Using jQuery, you can insert elements immediately after all the elements in the selection. Consider the code:
You can use the following simple code to insert a new item after each item in a list:
And here is what we get:
In D3, you have to go through all the elements of the selection and add them via JavaScript:
The best option is to make a general purpose function that adds elements based on the tag name and returns a new selection of the created elements so that they can then be edited:
By adding the following to your code, you can use the after () function in much the same way as in jQuery:
The before () function looks almost the same, with the only difference being that elements are inserted before the selection.
It's simple - the jQuery function deletes all child nodes in the selection.
And as a result:
In D3, for this you need to clear the internal HTML of the selected element:
D3 is often used to work with SVG. In this case, this code will not work, since innerHTML is not supported there. Therefore, it is better not to call html (), but to select all the child nodes and delete them:
General purpose code will be simple. I chose a different name for the function than that used in jQuery, since D3 already has its own empty () function.
Now you can clear the selection in much the same way as in jQuery:
In jQuery, this function works almost the same as the append () function in D3, but it adds the preceding selected items to another selection. To do this in D3, you need to go through all the elements in both selections and add the elements to each other. If you have several goals to which you need to add a selection, you will have to clone objects to get behavior similar to jQuery. That's what I did:
Using it, you can add multiple elements to the DOM. Work example:
Now call appendTo () on all elements that have the class “foo” to add them to the targets.
What will happen in the DOM:
The function returns the added elements so that you can work with them further. For example, changing the background:
d3.selectAll ('. Foo'). AppendTo ('. Target'). Style ('background', '# f00');
It is sometimes useful to know how many elements are in your selection. jQuery has a property called length
Same thing in D3:
With this code, you can do this:
As already mentioned, in D3, you can use the classed function to manage class names. But in D3 there is no function for switching class names, which is often used in jQuery. Its implementation may be as follows:
To filter a selection of several elements and select only a node with a given index, you can use the eq () function in jQuery. It is quite simple to do for D3. We make a subsample of the elements based on the index and return the newly made selection:
Used to change the visibility of an element on the page. They simply change the styles of the selected elements. And in the toggle () function, you first need to check whether this element is visible.
Show hidden:
Hide visible:
Toggle Visibility:
These features are often missing in D3, but they are not related to jQuery. D3 is often used to work with SVG. At the same time, unlike HTML, in SVG the order of elements determines their visibility. Therefore, we often do not have enough functionality to move the selection in the SVG backward or forward.
To do this, we expand the D3 samples with the following functions:
Using them is extremely simple - select the svg element and move it where necessary:
We hope that the above will be useful in those projects in which the excessive use of jQuery can be replaced by simple solutions on D3. And an already expanded version of D3 with the included features can be taken on GitHub.
And although there are powerful features in D3 - selectors and ajax wrappers, we often miss some functions from jQuery. We will show how jQuery can be replaced using D3 everywhere. As a result, your code will be simplified, the volume of the project will decrease, and you will not mix different approaches, but will use the functions as is customary in D3.
First, let's look at how these two libraries converge. This is convenient for those who already know jQuery and want to learn D3.
Similarities
Selectors
Both libraries are based on easy-to-use, but feature-rich selectors.
jQuery
$('.foo').addClass('foobar');
$('.foo').removeClass('foobar');
D3
d3.selectAll('.foo').classed('foobar', true);
d3.selectAll('.foo').classed('foobar', false);
Manage styles and attributes
jQuery
$('.foo').attr('data-type', 'foobar');
$('.foo').css('background', '#F00');
D3
d3.selectAll('.foo').attr('data-type', 'foobar');
d3.selectAll('.foo').style('background', '#F00');
Ajax
The syntax is slightly different, but both D3 and jQuery have good wrappers for
ajax.
jQuery
$.getJSON('http://url-to-resource.json', doSomething);
$.ajax({
url: 'http://url-to-resource.txt',
dataType: 'text',
type: 'GET',
success: doSomething
});
D3
d3.json('http://url-to-resource.json', doSomething);
d3.text('http://url-to-resource.txt', doSomething);
Class management
It is often necessary to manage DOM element classes, for example, to switch styles.
jQuery
$('.foo').addClass('foobar');
$('.foo').removeClass('foobar');
D3
d3.selectAll('.foo').classed('foobar', true);
d3.selectAll('.foo').classed('foobar', false);
Append and Prepend
Insertion of child nodes is an important function, especially when visualizing input data. This is easy and simple:
jQuery
$('.foo').append(''); $('.foo').prepend('');
D3
d3.selectAll('.foo').append('div');
d3.selectAll('.foo').insert('div');
Event tracking
The same syntax is designed to track events on selected elements.
jQuery
$('.foo').on('click', clickHandler);
D3
d3.selectAll('.foo').on('click', clickHandler);
Delete items
Sometimes you need to remove items from the DOM. Here's how to do it:
jQuery
$('.foo').remove();
D3
d3.selectAll('.foo').remove();
Subset Selection
You can select children from a larger
jQuery selection
$('.foo').find('.bar');
D3
d3.selectAll('.foo').selectAll('.bar');
Content management
You can use the following functions to modify the contents of a DOM node.
jQuery
$('.foo').text('Hello World!');
$('.foo').html('');
D3
d3.selectAll('.foo').text('Hello World!');
d3.selectAll('.foo').html('');
Differences
Now consider the features that are in jQuery but not in D3. For each of them, there is a simple solution to replace it, as well as a more general use case, which can be useful to you anywhere in your application, using branded D3 chains.
Activation of events and custom events (trigger events and custom events)
One of the advantages of jQuery is the convenience of working with events. You can trigger or track custom events for any item on the page. For example, you can start a custom event with some data for your document, and track it in code:
//слушаем
$(document).on('dataChange', function(evt, data) {
//do something with evt and data
console.log(data.newData);
});
//включаем событие
$(document).trigger('dataChange', {
newData: 'Hello World!'
});
In D3, this is not directly supported, but you can always achieve this behavior. A simple option (if you do not need d3.event in the handler):
//слушаем
d3.select(document).on('dataChange', function(data) {
console.log(d3.event); //null
console.log(data.newData);
});
//включаем событие
d3.select(document).on('dataChange')({
newData: 'Hello World!'
});
A more general approach is to add a function to the d3 object so that it can be used on any selection.
d3.selection.prototype.trigger = function(evtName, data) {
this.on(evtName)(data);
}
Adding this function to D3 allows you to get a trigger function that resembles that of jQuery, which can be used as follows:
d3.select(document).on('dataChange', function(data) {
console.log(data);
});
d3.select (document) .trigger ('dataChange', {newData: 'HelloWorld!'});
after () and before ()
Using jQuery, you can insert elements immediately after all the elements in the selection. Consider the code:
- List
- List
- List
You can use the following simple code to insert a new item after each item in a list:
$('li').after('Item ');
And here is what we get:
- List
- Item
- List
- Item
- List
- Item
In D3, you have to go through all the elements of the selection and add them via JavaScript:
d3.selectAll('li').each(function() {
var li = document.createElement('li');
li.textContent = 'Item';
this.parentNode.insertBefore(li, this.nextSibling);
})
The best option is to make a general purpose function that adds elements based on the tag name and returns a new selection of the created elements so that they can then be edited:
d3.selection.prototype.after = function(tagName) {
var elements = [];
this.each(function() {
var element = document.createElement(tagName);
this.parentNode.insertBefore(element, this.nextSibling);
elements.push(element);
});
return d3.selectAll(elements);
}
By adding the following to your code, you can use the after () function in much the same way as in jQuery:
d3.selectAll('li')
.after('li')
.text('Item')
//тут можно сделать со вставленными элементами что-нибудь ещё
The before () function looks almost the same, with the only difference being that elements are inserted before the selection.
d3.selection.prototype.before = function(tagName) {
var elements = [];
this.each(function() {
var element = document.createElement(tagName);
this.parentNode.insertBefore(element, this);
elements.push(element);
});
return d3.selectAll(elements);
}
empty ()
It's simple - the jQuery function deletes all child nodes in the selection.
- List item
- List item
- List item
$('ul').empty();
And as a result:
In D3, for this you need to clear the internal HTML of the selected element:
d3.selectAll('ul').html('');
D3 is often used to work with SVG. In this case, this code will not work, since innerHTML is not supported there. Therefore, it is better not to call html (), but to select all the child nodes and delete them:
d3.selectAll('ul').selectAll('*').remove();
General purpose code will be simple. I chose a different name for the function than that used in jQuery, since D3 already has its own empty () function.
d3.selection.prototype.clear = function() {
this.selectAll('*').remove();
return this;
}
Now you can clear the selection in much the same way as in jQuery:
d3.selectAll('#foo').clear();
appendTo ()
In jQuery, this function works almost the same as the append () function in D3, but it adds the preceding selected items to another selection. To do this in D3, you need to go through all the elements in both selections and add the elements to each other. If you have several goals to which you need to add a selection, you will have to clone objects to get behavior similar to jQuery. That's what I did:
d3.selection.prototype.appendTo = function(selector) {
var targets = d3.selectAll(selector),
targetCount = targets[0].length,
_this = this,
clones = [];
targets.each(function() {
var currTarget = this;
_this.each(function() {
if(targetCount > 1) {
var clone = this.cloneNode(true);
currTarget.appendChild(clone);
clones.push(clone);
}
else {
currTarget.appendChild(this);
}
});
});
if(targetCount > 1) {
this.remove();
}
return clones.length > 0 ? d3.selectAll(clones) : this;
}
Using it, you can add multiple elements to the DOM. Work example:
some elementsome other elementNow call appendTo () on all elements that have the class “foo” to add them to the targets.
d3.selectAll('.foo').appendTo('.target');
What will happen in the DOM:
some elementsome other elementsome elementsome other elementThe function returns the added elements so that you can work with them further. For example, changing the background:
d3.selectAll ('. Foo'). AppendTo ('. Target'). Style ('background', '# f00');
length ()
It is sometimes useful to know how many elements are in your selection. jQuery has a property called length
$('.foo').length; //2
Same thing in D3:
d3.selection.prototype.length = function() {
return this[0].length;
}
With this code, you can do this:
d3.selectAll('.foo').length() //2
toggleClass ()
As already mentioned, in D3, you can use the classed function to manage class names. But in D3 there is no function for switching class names, which is often used in jQuery. Its implementation may be as follows:
d3.selection.prototype.toggleClass = function(className) {
this.classed(className, !this.classed(className));
return this;
}
eq ()
To filter a selection of several elements and select only a node with a given index, you can use the eq () function in jQuery. It is quite simple to do for D3. We make a subsample of the elements based on the index and return the newly made selection:
d3.selection.prototype.eq = function(index) {
return d3.select(this[0][index]);
}
show () / hide () / toggle ()
Used to change the visibility of an element on the page. They simply change the styles of the selected elements. And in the toggle () function, you first need to check whether this element is visible.
Show hidden:
d3.selection.prototype.show = function() {
this.style('display', 'initial');
return this;
}
Hide visible:
d3.selection.prototype.hide = function() {
this.style('display', 'none');
return this;
}
Toggle Visibility:
d3.selection.prototype.toggle = function() {
var isHidden = this.style('display') == 'none';
return this.style('display', isHidden ? 'inherit' : 'none');
}
moveToFront (), moveToBack ()
These features are often missing in D3, but they are not related to jQuery. D3 is often used to work with SVG. At the same time, unlike HTML, in SVG the order of elements determines their visibility. Therefore, we often do not have enough functionality to move the selection in the SVG backward or forward.
To do this, we expand the D3 samples with the following functions:
d3.selection.prototype.moveToFront = function() {
return this.each(function(){
this.parentNode.appendChild(this);
});
};
d3.selection.prototype.moveToBack = function() {
return this.each(function() {
var firstChild = this.parentNode.firstChild;
if (firstChild) {
this.parentNode.insertBefore(this, firstChild);
}
});
};
Using them is extremely simple - select the svg element and move it where necessary:
d3.select('svg rect')
.moveToFront()
.moveToBack();
We hope that the above will be useful in those projects in which the excessive use of jQuery can be replaced by simple solutions on D3. And an already expanded version of D3 with the included features can be taken on GitHub.