简体   繁体   中英

Why does javascript render things

I'm a relative newcomer to JS / Laravel. I wrote this simple JS to add 2 fields, on demand, to my Laravel Blade page. I wanted both input boxes to be inline instead of one on top of the other. When I wrote the JS like this:

    $(document).ready(function() {
    var max_fields      = 4; //maximum input boxes allowed
    var wrapper         = $(".input_fields_wrap"); //Fields wrapper
    var add_button      = $(".add_field_button"); //Add button ID

    var x = 1; //initlal text box count
    $(add_button).click(function(e){ //on add input button click
        e.preventDefault();
        if(x <= max_fields){ //max input box allowed
                        $(wrapper).append(" <div class='form-inline'> "); 
            $(wrapper).append(" <div class='form-group'> ");
            $(wrapper).append(" <label for='products_id["+x+"]'>Product ID:</label> ");
            $(wrapper).append(" <input type='text' class='form-control' style='margin-left:5px' name='products_id["+x+"]'> "); 
            $(wrapper).append(" </div>"); 
            $(wrapper).append(" <div class='form-group'> ");
            $(wrapper).append(" <label for='quantity["+x+"]' style='margin-left:10px'>Quantity:</label> ");
            $(wrapper).append(" <input type='text' class='form-control' style='margin-left:5px' name='quantity["+x+"]'> ");
            $(wrapper).append(" </div> "); 
            $(wrapper).append(" </div> "); 

            x++; //text box increment
        }
    });

});

It didn't work - when it converts to HTML, the HTML renders out like this:

    <div class="input_fields_wrap">
    <button class="btn btn-primary add_field_button" style="margin-bottom:5px">Add More Fields</button>
    <div class="form-inline"> </div> 
    <div class="form-group"> </div> 
    <label for="products_id[1]">Product ID:</label>  
    <input type="text" class="form-control" style="margin-left:5px" name="products_id[1]">   
<div class="form-group"> </div> 
<label for="quantity[1]" style="margin-left:10px">Quantity:</label>  
<input type="text" 
class="form-control" style="margin-left:5px" name="quantity[1]">     </div>

The are not being placed where I want them to go above, screwing up all the CSS and not putting the text boxes inline like I wanted.

But, when I do this:

    $(add_button).click(function(e){ 
        e.preventDefault();
        if(x <= max_fields){ //max input box allowed
            $(wrapper).append(" <div class='form-inline'> <div class='form-group'> <label for='products_id["+x+"]'>Product ID:</label> <input type='text' class='form-control' style='margin-left:5px' name='products_id["+x+"]'> </div> <div class='form-group'> <label for='quantity["+x+"]' style='margin-left:10px'>Quantity:</label><input type='text' class='form-control' style='margin-left:5px' name='quantity["+x+"]'> </div> </div>"); 
            x++; //text box increment
        }
    });

});

It works.

<div class="input_fields_wrap">
<button class="btn btn-primary add_field_button" style="margin-bottom:5px">Add More Fields</button> 
<div class="form-inline"> 
<div class="form-group"> 
<label for="products_id[1]">Product ID:</label> 
<input type="text" class="form-control" style="margin-left:5px" name="products_id[1]"> 
</div> 
<div class="form-group"> 
<label for="quantity[1]" style="margin-left:10px">Quantity:</label>
<input type="text" class="form-control" style="margin-left:5px" name="quantity[1]"> 
</div> 
</div>
</div>

Whats the difference? What am I missing?

That's not how DOM works. In the eye of the browser each tag is an element, an instance of HTMLElement object. You can't add opening and closing tags separately. You are supposed to append an element . append method parses the passed string, creates the corresponding DOM elements and appends them to the specified location in the DOM.

If you pass a malformed html to it, you will get different results on different browsers. For example the .append("</div>"); call won't create any element on my Chromium browser as it's an invalid markup. The generated markup of the first script doesn't generate the expected markup as by coding that way all the elements become siblings, ie the generated elements are not nested elements.

This is one of way generating the expected markup:

var $col = $("<div class='form-inline'>") // the top level element of the collection
  .appendTo(wrapper) 
  .append('...') // the appended contents will be child of the `.form-inline` element

Now if you want to append element(s) to one of the descendants of .form-inline element you should at first query the DOM and select it:

$col.find('.aChild').append('...');

DOM is a tree .


Suggestion: If you want to generate many elements dynamically you can use a templating library. Maintaining this code without using a templating library can be a nightmare .

jQuery is building the DOM as you append.

When you add a div, eg:

$(wrapper).append(" <div class='form-group'> ");

jQuery is adding the div, and closing it . You can see this in your first result.

When you add an element, include its closing tag.

It is better if you build the html string first, then do the append all at once.

s="<div class='form-group'> "+
" <label for='products_id["+x+"]'>Product ID:</label> "+
" <input type='text' class='form-control' style='margin-left:5px' name='products_id["+x+"]'> "+
" </div>"+
" <div class='form-group'> "+
" <label for='quantity["+x+"]' style='margin-left:10px'>Quantity:</label> "+
" <input type='text' class='form-control' style='margin-left:5px' name='quantity["+x+"]'> "+
" </div> "+
" </div> "
 $(wrapper).append(s);

The jquery append method ( http://api.jquery.com/append/ ) interprets your string into an html element, and adds it to the DOM:

$(wrapper).append(" <div class='form-group'> ");

When you get around to adding the closing tag later on, it's too late. Also, when you append a div to the wrapper, you're appending it directly, meaning this line results in a new child element of the wrapper element:

 $(wrapper).append(" <input type='text' class='form-control' style='margin-left:5px' name='quantity["+x+"]'> ");

... and not a child of the form-group element.

If you want your elements to be nested, you have to append them in a nested way, each to its parent, and not a distant ancestor. If you want to add a big chunk of html, you have to do it in one go so that it can be interpreted as a big blob of elements all at once.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM