2

I'm working on removing all jQuery code from an application in place of pure Javascript. I've been working through it and making the changes which are working fine, but the code itself seems a lot "more" than jQuery.

I get the feeling that there is a much cleaner way to write this code than my approach.

Below is an extract from the code:

var aTestUl = document.createElement('ul');
var aTestLi = document.createElement('li');

var bTestBtn = document.createElement('a');
bTestBtn.className = "gc_bTest";
bTestBtn.id = "gc_bTest_" + aTestVarId;
bTestBtn.href = "#";
bTestBtn.dataset.body = aTestVarBody;
bTestBtn.innerHTML = "Call test function 1";

var bTestCTestDiv = document.createElement('span');
bTestCTestDiv.innerHTML = " - ";

var cTestBtn = document.createElement('a');
cTestBtn.className = "gc_cTest";
cTestBtn.id = "gc_cTest_" + aTestVarId;
cTestBtn.href = "#";
cTestBtn.dataset.body = aTestVarBody;
cTestBtn.innerHTML = "Call test function 2";

var aTestText = document.createTextNode(aTestVarId + ' - '
    + aTestVarBody + ' - ');

aTestLi.appendChild(aTestText);
aTestLi.appendChild(bTestBtn);
aTestLi.appendChild(bTestCTestDiv);
aTestLi.appendChild(cTestBtn);
aTestUl.appendChild(aTestLi);
document.getElementById('gc_aTest_list').appendChild(aTestUl);

This essentially creates a list with a heading: "someId - someBody - Function 1 href - Function 2 href". There is some more code not included which appends another ul and li... to the end of aTestLi before it appends to aTestUl.

This was just a few lines of code in jQuery. Is there a much simpler way to do this?

As a breakdown, incase the code is a bit confusing, it does the following:

1. Create a master <ul> to append to the master <div>
2. Create a list <li> to append to the master <ul>
3. Create some text to put in the <li>
4. Create an <a> element (with attributes, such as id, class, ...)
5. Create another <a> element (with similar attributes)
6. Put the text, hyphens and the two <a> elements in the list
(<li>TEXT - <a /> - <a /></li>)
7. Attach the <li> to the master <ul>
(<ul><li>TEXT - <a /> - <a /></li></ul>)
8. Attach the master <ul> to the master <div>
(<div><ul><li>TEXT - <a /> - <a /></li></ul></div>)

This is something the code does quite often in the application so if it can be simpler would be great.

Thanks!

EDIT: jQuery and other external libraries are not valid for this application due to certain requirements.

EDIT: To use correct terminology, "Vanilla Javascript" is the requirement.

EDIT: HTML output

<div class="gc_list" id="gc_profile_list">
    <ul>
        <li>
            ID9723 - AA00BB11CC2 - 
            <a class="gc_f_values" id="gc_f_values_ID9723" href="#" data-device="AA00BB11CC2">Get F Values</a>
            <span> - </span>
            <a class="gc_i_values" id="gc_i_values_ID9723" href="#" data-device="AA00BB11CC2">Get I Values</a>
            <ul>
                <li>Action: Save</li>
                <li>Action: Delete</li>
                <li>Action: Archive</li>
                <li>Action: Pass</li>
            </ul>
        </li>
    </ul>
</div>
17
  • What is aTestVarId & aTestVarBody? Essentially, when you check the code, you see that you are repeating some steps (eg, creating the element and setting some properties). So you could choose to extract that functionality into separate functions and do the same for other repeating codes (eg, appending a list of children to an element) Commented Mar 15, 2016 at 15:11
  • 1
    "...but the code itself seems a lot 'more' than jQuery" yes, that's exactly the point of jQuery - to simplify DOM manipulations by providing an elegantly designed API. If you want to keep the simplicity without the dependency on jQuery, you'll probably end up building your own DOM library. Commented Mar 15, 2016 at 15:11
  • 2
    Any reason why you don't want to use jQuery? The slogan of jQuery is "write less, do more" so it's not that odd that it looks like "more" this way. Anyways, this question is probably a bit too much opinion based for Stack Overflow. Commented Mar 15, 2016 at 15:11
  • 2
    Clean-up is definitely required here. I'd recommend doing that first and worry about porting later (if at all). I'm not sure moving this to Code Review is a good idea though, it looks like example code and we don't do that. Commented Mar 15, 2016 at 15:14
  • 1
    @Ivar Where we are deploying the application, jQuery can not be used (requirements from the client, etc) Commented Mar 15, 2016 at 15:16

3 Answers 3

2

First of all, JavaScript is a functional language. and like any functional language, the following rules apply

  1. Don't Repeat yourself
  2. Treat Functions like Values
  3. Favor Expressions over statements

Following the rules above and you'll end up with a micro jQuery-ish library. That was what @Mitch Karajohn was talking about. I did some minor refactoring so that you can see where I'm going with this. The resulting code will be cleaner and easier to modify/reuse

function CreateTestElement(element, options) {
    document.createElement(element);
    if (options) {
        element.className = options.className;
        element.id = options.className + "_" + options.id;
        element.href = options.href;
        element.dataset.body = options.body;
        element.innerHTML = options.innerHTML;
    }
    return element;
}

function CreateText(text){
    return document.createTextNode(text);
}

var bTestBtn = CreateElement('a', {
    className: "gc_bTest",
    id: aTestVarId,
    href: "#",
    body: aTestVarBody,
    innerHTML: "Call test function 1"
});

var bTestCTestDiv = CreateElement('span', {
    innerHTML: " - "
});

var cTestBtn = CreateElement('a', {
    className: "gc_cTest",
    id: aTestVarId,
    href: "#",
    body: aTestVarBody,
    innerHTML: "Call test function 2"
});

var aTestUl = CreateElement('ul')
                .appendChild(CreateElement('li'))
                .appendChild(CreateText(aTestVarId + ' - ' + aTestVarBody + ' - '))
                .appendChild(bTestBtn)
                .appendChild(bTestCTestDiv)
                .appendChild(cTestBtn);

document.getElementById('gc_aTest_list').appendChild(aTestUl);
Sign up to request clarification or add additional context in comments.

3 Comments

This looks much neater than what I have, and importantly it is simple to follow what's going on. I'll definitely do something like this instead of my approach, like you said, repeating yourself is no good in any code.
Maybe one change could be that options are treated dynamically (eg: for (var prop in options) { element[prop] = options[prop] }) It could make the creation of the element a lot more dynamic?
That's equally good. The only reason I didn't do it was because of the dataset property. Doing so would erase other properties of dataset. Typically I'd write an extend() method that would do a deep assignment of object projects then "extend" elements with the options object
1

This was just a few lines of code in jQuery. Is there a much simpler way to do this?

With pure JavaScript? No. JQuery's whole thing is that it simplifies such stuff, allowing you to 'write less' to achieve the same results you would achieve with pure JS.

I get the feeling that there is a much cleaner way to write this code than my approach.

There is not, without using some other library (or writing you own).

12 Comments

ofcourse there is a way to write it cleaner, you extract the code that repeats certain actions
Yeah, sure, but I don't think OP's question was about loops and functions.
@Icepickle I did look at doing that actually and was planning to create a common function to call where I can do so. But even if I do that, I would still have a version of the above code in there. I understand that jQuery is meant to shorten Javascript so you don't have to write lines and lines of code, but the above just seemed a little "too much" more than I had expected
jQuery also has considerable bulk - there is the trade off of adding a large library for a few lines to be considered. That said, creating DOM elements from scratch is one of the things vanilla js really sucks at still..
Just to make it clear, I am not advocating jQuery or something. @Gary simply asked if he is able to condense his vanilla JS code to sizes similar to jQuery code, doing the same thing (at least this is how I interpreted the question), to which the answer is simply 'No'. Of course jQuery has an overhead, of course he can do loops and functions, but he will still have to write this stuff, on his own, while jQuery provides him with a ready API out of the box.
|
1

For code compactness (though not necessarily readability), sometimes it makes smaller code to just generate the HTML string and then let an assignment to .innerHTML create most of the DOM nodes for you.

If you have repetitive HTML, you can then put the generation of the repetitive portions into a single function and call it several times with different arguments. Here's your example done this way:

var aTestUl = document.createElement('ul');
var aTestLi = document.createElement('li');
var actions = "<ul><li>Action: Save</li><li>Action: Delete</li><li>Action: Archive</li><li>Action: Pass</li></ul>";

function createButton(cls, id, body, msg) {
    return '<a href="#" class="' cls + '" id="' + id +
           '" data-body="' + body + '">' + msg + '</a>';
}

var liHTML = aTestVarId + ' - ' + aTestVarBody + ' - ' +
  createButton("gc_bTest", "gc_bTest_" + aTestVarId, aTestVarBody, "Call test function 1") +
  '<span> - </span>' +
  createButton("gc_cTest", "gc_cTest_" + aTestVarId, aTestVarBody, "Call test function 2") + 
  actions;

aTestLi.innerHTML = liHTML;
aTestUl.appendChild(aTestLi);
document.getElementById('gc_aTest_list').appendChild(aTestUl);

FYI, this type of text manipulation would be great for text replacement in ES6 template strings if you were running an environment where that was supported.

2 Comments

So essentially, instead of creating each element and node you define it as a string variable then assign it into "innerHTML"?
@Gary - Yes, that's one shortcut. You let the browser create all the child nodes for you directly from some generated HTML.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.