1
\$\begingroup\$

I have the following in script in my web app which contains a lot of duplication. How can I cut down on this duplication and generally improve my code?

$('a#newAuthority').on("click", function() {
    var $newAuthorityContainer = $(".new-authority-container");

    var $row = $('<div />', {
        class: 'row'
    }).appendTo($newAuthorityContainer);

    var $large11 = $('<div />', {
        class: 'large-11 columns'
    }).appendTo($row);

    var $input = $('<input>', {
        type: 'text',
        placeholder: 'email@domain.com',
        class: 'authority-email'
    }).appendTo($large11);

    var $large1 = $('<div />', {
        class: 'large-1 columns'
    }).appendTo($row);

    var $removeContainer = $('<div />', {
        class: 'remove-container'
    }).appendTo($large1);

    var $remove = $('<a>', {
        href: '#',
        class: 'remove'
    }).html('<i class="fa fa-fa fa-remove"></i>').appendTo(
        $removeContainer);
    return false;
});
$('a#newMember').on("click", function() {
    var $newMemberContainer = $(".new-member-container");

    var $row = $('<div />', {
        class: 'row'
    }).appendTo($newMemberContainer);

    var $large11 = $('<div />', {
        class: 'large-11 columns'
    }).appendTo($row);

    var $input = $('<input>', {
        type: 'text',
        placeholder: 'email@domain.com',
        class: 'authority-email'
    }).appendTo($large11);

    var $large1 = $('<div />', {
        class: 'large-1 columns'
    }).appendTo($row);

    var $removeContainer = $('<div />', {
        class: 'remove-container'
    }).appendTo($large1);

    var $remove = $('<a>', {
        href: '#',
        class: 'remove'
    }).html('<i class="fa fa-fa fa-remove"></i>').appendTo(
        $removeContainer);
    return false;
});

$('input.button').on("click", function() {

    var $authorityInputs = $('.authority-email');

    var authortyEmails = [].map.call($authorityInputs, function(
        $authorityInputs) {
        return $authorityInputs.value;
    }).join(',');

    if ($('body.teamsnew').length) {
        $('input#team_authority_emails').val(authortyEmails);
    }

    if ($('body.teamsedit').length) {
        $('input#team_authority_emails').val('{' + authortyEmails + '}');
    }

    var $memberInputs = $('.member-email');

    var memberEmails = [].map.call($memberInputs, function(
        $memberInputs) {
        return $memberInputs.value;
    }).join(',');

    if ($('body.teamsnew').length) {
        $('input#team_member_emails').val(memberEmails);
    }

    if ($('body.teamsedit').length) {
        $('input#team_member_emails').val('{' + memberEmails + '}');
    }
});

$(document).on('click', '.remove', function() { 
    $(this).closest('.row').remove();
    return false;
});
\$\endgroup\$
1
\$\begingroup\$

Extract the code that creates a row to a function, make it return the row created with all the elements inside of it, so that you can use it like this:

var $newAuthorityContainer = $(".new-authority-container");
var $row = createRow().appendTo($newAuthorityContainer);

And then again later:

var $newMemberContainer = $(".new-member-container");
var $row = createRow().appendTo($newMemberContainer);

Alternatively, pass in the container that you want to add the row to, for example:

var $newMemberContainer = $(".new-member-container");
createRow($newMemberContainer);

The function for this latter example:

function createRow($container) {
    var $row = $('<div />', {
        class: 'row'
    }).appendTo($container);

    var $large11 = $('<div />', {
        class: 'large-11 columns'
    }).appendTo($row);

    var $input = $('<input>', {
        type: 'text',
        placeholder: 'email@domain.com',
        class: 'authority-email'
    }).appendTo($large11);

    var $large1 = $('<div />', {
        class: 'large-1 columns'
    }).appendTo($row);

    var $removeContainer = $('<div />', {
        class: 'remove-container'
    }).appendTo($large1);

    var $remove = $('<a>', {
        href: '#',
        class: 'remove'
    }).html('<i class="fa fa-fa fa-remove"></i>').appendTo(
        $removeContainer);
}

I don't see other troubling duplication.

However, I see a bit troubling typo in the variable name authortyEmails. This could lead to errors, so I suggest to fix that.

\$\endgroup\$
  • \$\begingroup\$ Thanks @janos. Can you see any other improvements, gotchas that I could use to improve this script? \$\endgroup\$ – Thomas Taylor Jun 17 '15 at 6:24
  • \$\begingroup\$ I just found a semantic error in my code. The problem with your solution (now the code has changed) is that the var $input class name changes. Ideally I'd like to be able to DRY out the HTML creation and append the layout with the correct class to the correct container. \$\endgroup\$ – Thomas Taylor Jun 17 '15 at 6:41
1
\$\begingroup\$

General advice on code duplication is to detect pieces of duplicated code and extract them into separate, reusable, functions.

Example:

 var $row = $('<div />', {
    class: 'row'
}).appendTo($newMemberContainer);

Create row logic can be extraced:

function createRow() {
  return $('<div />', {
    class: 'row'
  });
}

and the whole piece of code would look like:

createRow().appendTo($newMemberContainer);
createRow().appendTo($newAuthorityContainer);

Sometimes you'll probably need to parametrize the functions. In your case if you have larger blocks, you can create functions for creating whole components.

And a bit off topic maybe, but if you do a lot of HTML creation / modification in your JS, you probably should probably consider switching to knockout / angularJS, which is much better than pure jQuery manipulations.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.