The DOM is getting better

Making Wengit #2

This is the second entry of my Making Wengit series. If you haven’t read the introduction article, you should do so first.

I’m well on the way to finishing the front-end development of wengit, at least until I do a complete overhaul, as I probably will, because I’m crazy like that. Anyways, check out the code here, if you want.

I have made the conscious decision to not use jQuery or any other DOM library, and to only utilize features built into Riot.js or in the vanilla DOM. In my labors, so far I have discovered many nice features of the DOM which I previously were not aware existed.

Many of this newer DOM functionality is very close to jQuery methods, and were probably based upon jQuery’s ease of use.

Element.classList

This essentially contains all of the functionality of jQuery’s .hasClass.addClass.toggleClass, and .removeClass. For instance, the following code segments are equivalent:

// jQuery
$("#element_id").toggleClass("some_class", some_value);
// Vanilla DOM
document.getElementById("element_id")
.classList.toggle("some_class", some_value);

This is how the jQuery methods correlate with the classList methods:

jQuery       Vanilla DOM
-------------------------------
addClass classList.add
hasClass classList.contains
removeClass classList.remove
toggleClass classList.toggle

Handy, right?

Node.contains, Element.matches, Element.closest

These are super awesome. contains take an element as an argument and checks if it is the descendant of the Node. matches takes a CSS selector as an argument and checks if the Element matches that selector. closest takes a CSS selector as an argument and returns the closest parent that matches the selector.

These are very helpful for event delegation, because this is now an event delegation function in Vanilla DOM:

function delegate(element, event, descendentSelector, callback){
element.addEventListener(event, function(e){
var elem = e.target.closest(descendentSelector);
// returns null if no matching parentNode is found
if(elem){
callback(elem, e);
}
}, false);
}

That’s much easier than looping through the parentNodes of an element and checking if it matches the selector given.

Some handy tweaks

I know Firefox says not to modify the prototypes of built-in objects, but when it’s so easy, and so helpful, why not? I use this little piece of code to make my life easier:

// allows the use of for ... of loops on NodeLists
NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
// shortcut for addEventListener
Element.prototype.on = function(eventType, callback){
this.addEventListener(eventType, callback, false);
};
// shortcut for removeEventListener
Element.prototype.off = function(eventType, callback){
this.removeEventListener(eventType, callback, false);
};
// watch event one time
Element.prototype.one = function(eventType, callback){
this.on(eventType, function cb(){
this.off(eventType, cb);
callback(...arguments);
});
};
window.$$ = document.querySelectorAll.bind(document);

For … of

This is super handy. Essentially, for … of iterates over any Iterable’s values. Adding the iterator from Array to NodeList allows this super handy syntax, at least in any ECMAScript 2015 capable browser (which includes Electron, of course). It makes, for instance, doing the equivalent of jQuery.each a lot easier. I also prefer this way to forEach because it is less code and has continue and break functionality, plus return statements refer to the outer function, not the inner one. It makes my job a lot easier.

There is one catch though. I don’t recommend using this with cached live NodeLists, like those returned by getElementsByClassName and getElementsByTagName, as their contents could change depending on the state of the DOM. Using querySelector and querySelectorAll is safe, though, as they return static NodeLists.

My Best Practices

I try to stick to these practices when working with the DOM:

  1. Use CSS for visual effects and positioning whenever possible
    This separates the visual portion of your application from the functional part. Instead of using JavaScript to toggle styles, toggle a CSS class instead. It makes it much easier to modify the look and feel in the future.
  2. Use separate files for each module, each self-contained with styles, programming, and markup
    This allows for easier modification of individual modules, as changes within one file will not affect other modules. It also makes Git diffs much nicer. You can use concatenation and minification in production.
  3. Comment everything
    I shouldn’t have to say this, but there should be comments at least accompanying every JavaScript function, at the beginning of every module, and explaining every CSS class. I, personally, will comment almost every line of code, so that contributors and beginners can help and learn more easily.
  4. Only use what you need
    Don’t be afraid to craft your own fix or utility. If you’re only going to use a couple functions, there is no reason to include a whole library.
  5. Play to your strengths
    This is especially helpful when working with others. Specialization is key. If you’re good at design, work on design. If you’re good at programming, work on that. Let others fill in the gaps. You can’t do it all perfectly.

Update Time

I know it’s been awhile since I wrote the last update. I’ve fallen way behind since getting my new job. I recently got a new schedule, so I should be able to get some more work done on this project in the coming weeks.

Anyways, the project is going well so far. I’ve finished a few components, including the dropdown, tabs, custom select, filter input, and directory browsing modules. The window buttons work, minimizing, maximizing, and closing the window as they should.


Please Consider Contributing

Even if you can’t code, it would be useful to have help on this project. If you want to help me build this useful, open-source utility, please open an issue on the Github repo detailing any modifications, improvements, or features you can think of. If you can, code a little, and submit a pull request.

Any help is less work for me, and a faster creation of the program.