3
\$\begingroup\$

I am a JavaScript beginner. Here's a fictional example which isolates the functionality mentioned in the subject line. I hope to employ this functionality as part of a larger web application in the real world soon.

Live example here

An HTML5 page containing a simple form with one row:

<!doctype html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>Add Elements to a Form</title>
    <!--[if lt IE 9]>
    <script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script>
    <![endif]-->
    <link rel="stylesheet" href="css/styles.css">
</head>
<body>
<div id="addrow">
    <label for="addnum">Add Rows:
    <input type="text" name="addnum" id="addnum" value="" size="2">
    </label>
    <input type="button" name="addbtn" id="addbtn" value="Go!">
</div>
<br>
<fieldset id="lunches"> 
    <legend>Let's do Lunch</legend>
    <div class="formrow">
        <span>My </span>
        <select name="lunch[]" class="dropdown">
            <option value="">Select a lunch item</option>
            <option value="baloney">Baloney</option>
            <option value="yogurt">Yogurt</option>
            <option value="apple">Apple</option>
            <option value="cheese">Government Cheese</option>
        </select>
        <span> has a </span>
        <select name="firstorlast[]" class="dropdown">
            <option value="first">First</option>
            <option value="second">Second</option>
        </select>
        <span> name. It's</span>
        <input type="text" name="name[]" value="Specify a Name" size="20">
    </div>
</fieldset>

<script src="js/utilities.js"></script>
<script src="js/addelements.js"></script>
</body>
</html>

addelements.js which contains the functionality for adding and removing rows of input. Each added row will have a button which, when clicked, will delete the row from the form. The first row cannot and should not be deleted:

    window.onload = function() {
    'use strict';
    //U.addEvent is a cross-browser way to add an event handler to a DOM object.
    //U.$ is a shortcut to getElementById
    //Idea taken from Larry Ullman's "Modern Javascript: Develop and Design
    U.addEvent(U.$('addbtn'), 'click', addRows);
}

function addRows() {
    'use strict';
    var numRowsToAdd = U.$('addnum').value;
    for(var i = 0; i < numRowsToAdd; i++){
        addRow();
    }
}

function addRow() {
    'use strict';
    var sourceNode = document.querySelector('.formrow');
    var newRow = sourceNode.cloneNode(true);
    //every row except the first row should have a delete button associated with it.
    var delButton = document.createElement('input');
    delButton.className = 'delbtn';
    delButton.type = 'button';
    delButton.name = 'delbtn';
    delButton.value = 'Delete This Row';
    U.addEvent(delButton, 'click', function() {removeRow(delButton)});
    newRow.appendChild(delButton);
    var fieldset = U.$('lunches');
    fieldset.appendChild(newRow);
}

function removeRow(obj) {
    'use strict';
    var theRow = obj.parentNode;
    var theRowParent = theRow.parentNode;
    theRowParent.removeChild(theRow);
}

relevant code from utilities.js:

    var U = {

    // For getting the document element by ID:
    $: function(id) {
        'use strict';
        if (typeof id == 'string') {
            return document.getElementById(id);
        }
    },

    addEvent: function(obj, type, fn) {
        'use strict';
        if (obj && obj.addEventListener) { // W3C
            obj.addEventListener(type, fn, false);
        } else if (obj && obj.attachEvent) { // Older IE
            obj.attachEvent('on' + type, fn);
        }
    }   
}

If you copy and save these into the proper files with the proper path structure, it should work for you.

\$\endgroup\$

2 Answers 2

2
\$\begingroup\$

You should probably have a look at https://stackoverflow.com/questions/5805059/select-placeholder#answer-8442831 and make the first line non-selectable by HTML only.

It's done by making the first element selected, disabled and by adding a display:none.

\$\endgroup\$
2
  • \$\begingroup\$ As soon as I'm able to vote up your answer, I will. \$\endgroup\$
    – DeeDee
    Commented Jan 8, 2013 at 17:28
  • \$\begingroup\$ @DeeDee I think you can accept it if you think it answered your question (tick box under the score). \$\endgroup\$ Commented Feb 18, 2013 at 19:59
2
\$\begingroup\$

I would suggest using event delegation instead of adding event handlers to each 'Delete' button individually.

This can be done by attaching a click event listener to a parent element, then filtering the event target for a specified className.

For example, if we added a function U.on() as follows (and utilizing U.addEvent() that you already have):

!(function () {

'use strict';

/**
 * helper function to delegate/attach DOM events
 * @param  {String}   root  selector for event root element
 * @param  {String}   event single event name
 * @param  {String}   child className of target element
 * @param  {Function} fn    event handler
 */
function on(root, eventName, child, fn) {
    var to = document.querySelector(root);
    var handler = delegator(child, fn);
    U.addEvent(to, eventName, handler);
}

/**
 * fires event handler if event target matches className
 * @param  {String}   className class to match
 * @param  {Function} fn        event handler to call
 */
function delegator(className, fn) {
    return function (e) {
        if (e.target.classList.contains(className)) {
            fn.call(this, e);
        }
    };
}

U.on = on;

})();

We could delegate all of the 'Delete' button click events to the parent form element, by doing to following:

on('#lunches', 'click', 'delbtn', function (e) {
    removeRow(e.target);
});

This basically says, "If a click happens inside an element with the id lunches, and the click happened on an element with the class .delbtn, then call this function that I'm passing in as the last argument."

\$\endgroup\$

Your Answer

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

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