0
\$\begingroup\$

I am working on an online ordering form and am looking for some advice about my jQuery. I am re-developing the code base for this ordering form and improving what was created by a web design group years ago. The existing form uses Javascript to dynamically generate a new line of item information when the starts typing in the form. Reviewing the existing code there is close to 100 lines of JS (many being DOM manipulations) to add the HTML elements and so forth. I could redo it in JS no problem, but I would like to reduce this amount of code and convert the solution to jQuery. It is already in use for other parts of the site so I may as well utilize it.

I've written a function to facilitate this that I am toying around with to get the functionality down.

function createNewRow() {
    //createNewRow.rc = 1;  
    if(typeof createNewRow.rc == 'undefined') {
        createNewRow.rc = 1;
    }
    //Select the existing parent div
    var div = $('div#div');
    //Create a child div (container to the form elements)
    var cell = $('<div class="block itemRow"></div>');
    //Create "cells" (really divs) and form elements

    itemQty = $('<div class="itemQty"></div>');
    itemQty.append('<p>Quantity<p>')
           .append('<input type="text" size="5" class="inputbox" name="ooItem[' + createNewRow.rc + '][qty]" id="' + createNewRow.rc + '" />');

    itemUnit = $('<div class="itemUnit"></div>');
    itemUnit.append('<p>UOM</p>');
    unitSlct = $('<select name="ooItem[' + createNewRow.rc + '][unit]"></select>');
    unitSlct.append('<option value="pc">PCS</option>')
            .append('<option value="sq">SQ</option>')
            .append('<option value="lf">LF</option>')
            .append('<option value="ea">EA</option>');
    itemUnit.append(unitSlct);


    itemDesc = $('<div class="itemDescription"></div>');
    itemDesc.append('<p>Description</p>')
            .append('<input type="text" class="inputbox" name="ooItem[' + createNewRow.rc +'][description]" />');

    itemColor = $('<div class="itemColor"></div>');
    itemColor.append('<p>Color</p>')
             .append('<input type="text" class="inputbox" name="ooItem[' + createNewRow.rc +'][color]" />');

    itemText = $('<div class="itemTexture">');
    itemText.append('<p>Texture</p>');
    textSlct = $('<select class="inputbox" name="ooItem[' + createNewRow.rc +'][texture]"></select>');
    textSlct.append('<option value="rough">Rough</option>')
            .append('<option value="smooth">Smooth</option>');
    itemText.append(textSlct);

    cell.append(itemQty)
        .append(itemUnit)
        .append(itemDesc)
        .append(itemColor)
        .append(itemText);


    //Append child div to the parent div
    div.append(cell);
    //Increase the row count
    createNewRow.rc++;
}

JSFiddle is here: http://jsfiddle.net/McFRV/3/

From a functionality standoint this works fine. HTML rows are created as I like (minus the presentation layer of course) and the form information passes to the webserver correctly with all the information in the right places. What is bugging me a bit is how "dirty" this code appears to me. It seems as if using jQuery to make all that HTML in a bunch of strings is uncanny. Is there a better way to create chunks of HTML other than how I am going about it, or am I simply stressing about nothing?

\$\endgroup\$
2
\$\begingroup\$

You could always clean it up using jQuery tmpl

https://github.com/BorisMoore/jquery-tmpl

You would just define the HTML of the templates, and then you could add them to the rows.

\$\endgroup\$
  • \$\begingroup\$ Thanks for the answer. It does appear that library would help me clean up my particular code, but would also require the that I include another library (almost 500 lines) to do so. I would like to keep the third party libraries to a minimum. I am only using this for one page on the site. If I needed this for several areas I would take it under more consideration to utilize that library. \$\endgroup\$ – Crackertastic Oct 14 '13 at 18:03
  • \$\begingroup\$ You could technically do something similar using regular expressions to replace variable names. Then you don't have another library. \$\endgroup\$ – Jeremy Boyd Oct 14 '13 at 20:14
  • 2
    \$\begingroup\$ @Crackertastic Adding a few KBs of code should not be an issue. In fact, the template library is just 4KB! In the first place, you are using jQuery which is 40KB by itself. Also, creating HTML in this manner, you'd be creating more code than a template library and a template combined, not to mention very unreadable and hard to maintain. \$\endgroup\$ – Joseph Oct 15 '13 at 4:11
  • \$\begingroup\$ Thanks for the answers. After implementing into my site and removing the irrelevant .append()'s I found my code to be about 20 lines, which is acceptable. @JosephtheDreamer as I said in my above comment, I'm trying to keep the third party libraries to a minimum. I'm only using jquery because it is already there for some other plugins, otherwise I'd be doing this in native JS. However, I am going to keep that lib in mind for future projects where I need to generate a lot of HTML. In this particular case I needed to create just a few elements (and add more on demand) which I have done. \$\endgroup\$ – Crackertastic Oct 15 '13 at 19:17
1
\$\begingroup\$

You could abstract a lot of that out into functions to create the specific elements so instead of manipulating text strings, you call something like:

var itemDesc = createElement('div', {'class': 'itemDescription'});

With the function returning an element with the passed attributes set. You could then use a documentFragment to stage all the elements before they are drawn into the page. This would save some redraws and allow you to set up events, etc. on the elements while they're staged.

\$\endgroup\$
1
\$\begingroup\$

I took the liberty of rewriting your code, That's how'd do it any way so enjoy and feel free to ask if you have any questions :).

Creating item rows using knockout

This would be roughly 10 lines of code.

Code (In case link expire) :

HTML:

<html>

<head>
    <script type="text/javascript" src="//code.jquery.com/jquery-1.9.1.js"></script>
    <script type="text/javascript" src="http://knockoutjs.com/downloads/knockout-2.3.0.js"></script>
    <style type="text/css">
        div, input, button {
            display: inline;
            float: left;
        }

        .block {
            display: block;
            clear: both;
        }

        .inputbox {
            border: 1px solid #666;
        }
    </style>
</head>

<body style="zoom: 1;">
    <form>
        <input type="button" value="Make Row" data-bind="click: createNewRow" />
        <input type="submit" name="Submit" />
        <div id="div" data-bind="foreach: itemRow">
            <div class='block itemRow' data-bind="attr: { id: rowId }">
                <div class='itemQty'>
                    <p>Quantity</p>
                    <p></p>
                    <input type='text' size='5' class='inputbox' name='ooItem[1][qty]' id='1'>
                </div>
                <div class='itemUnit'>
                    <p>UOM</p>
                    <select name='ooItem[1][unit]'>
                        <option value='pc'>PCS</option>
                        <option value='sq'>SQ</option>
                        <option value='lf'>LF</option>
                        <option value='ea'>EA</option>
                    </select>
                </div>
                <div class='itemDescription'>
                    <p>Description</p>
                    <input type='text' class='inputbox' name='ooItem[1][description]'>
                </div>
                <div class='itemColor'>
                    <p>Color</p>
                    <input type='text' class='inputbox' name='ooItem[1][color]'>
                </div>
                <div class='itemTexture'>
                    <p>Texture</p>
                    <select class='inputbox' name='ooItem[1][texture]'>
                        <option value='rough'>Rough</option>
                        <option value='smooth'>Smooth</option>
                    </select>
                </div>
            </div>
        </div>
    </form>
    <script type="text/javascript" src="appCode.js"></script>
</body>

</html>

Javascript:

function MyViewModel(data) {
    var self = this;
    self.itemRow = ko.observableArray();
    self.createNewRow = function () {
        self.itemRow.push({
            rowId: self.itemRow().length
        });
    };
}

ko.applyBindings(new MyViewModel());
\$\endgroup\$
  • \$\begingroup\$ Thanks for that, it was a neat little piece of code to check out. I have a lot to learn in terms of these libraries. I'll be sure to have a look at that one for future use. Thanks again. \$\endgroup\$ – Crackertastic Oct 18 '13 at 19:59
  • \$\begingroup\$ Glad, i can help :) \$\endgroup\$ – Rue Ryuzaki Oct 18 '13 at 20:06
  • 1
    \$\begingroup\$ Could you include the code into the answer? Links can be dead later which would not help future readers. \$\endgroup\$ – palacsint Oct 27 '13 at 7:36

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.