SitePoint Sponsor

User Tag List

Page 1 of 2 12 LastLast
Results 1 to 25 of 31
  1. #1
    SitePoint Member
    Join Date
    Dec 2013
    Posts
    16
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    Javascript/jQuery problems

    Hi, I'm in the process of learning javascript and the jquery framework. I'm developing a simple "invoice generator" for learning purposes, and I came across some difficulties based on me being a novice. The main problem is maybe that the code is poorly formatted I think. It should be more modular... just don't know how.

    In this app: http://brunofelicio.com/staging/facturame/ when you change the values the totals also change, but if you change the taxes below it show a NaN error...

    Can you help?

    This is the javascript code (basically I check the values from the input fields and do the math):

    Code:
    $(document).ready(function(){
    	var index = 0;
    	
    	//Add row
    	var numRows = 0;
    	$(".add-row").click(function() {
    		numRows++;
    		console.log( "There are: " + numRows + " rows");
    		$(this).parent().parent().before("<tr class='row'><td><a href='#' class='delete-row'>Delete</a></td><td><input type='text' name='description' placeholder='Descripción del articulo' size='40' value=''/></td><td><input class='cantidad' type='text' name='cantidad' placeholder='Cant.' size='2' value=''/></td><td><input class='coste' type='text' name='coste' placeholder='Coste' size='2'/></td><td><p class='precio'>0</p></td></tr></tr>");
    	});
    	
    	//Delete row
    	
    	$(".delete-row").live("click", function() {
    		
    		$(this).parent().parent().remove();
    		numRows--;
    		
    		console.log( "There are: " + numRows + " rows");
    		console.log( "array: " + lineTotalArray);
    	});
    		
    
    	//Calculate
    	
    	$("input").live("keyup",function() {
    	
    		//Check values in the input boxes
    		var coste = $(this).parent().closest('tr').find(".coste").val();
    		var cantidad = $(this).parent().closest('tr').find(".cantidad").val();
    		var iva = $(".iva").val();
    		var irpf = $(".irpf").val();
    		
    		//Add Percentage
    		var per_iva = Number("0." + iva);
    		var per_irpf = Number( "0." + irpf);
    		var subtotal = 0;
    		var valueArray = [];
    		
    		//Totals
    		
    		var lineTotal = Number(cantidad * coste);
    		$("tr.row").find(".precio").eq(index).text(lineTotal);
    		
    		for (i=0; i<=numRows; i++ ){
    			
    			valueArray[i] = Number($("tr.row").find(".precio").eq(i).text());
    			subtotal += parseInt(valueArray[i]);
    		}
    		
    		var total_iva = subtotal * per_iva;
    		var total_irpf = subtotal * per_irpf;
    		
    		total = subtotal + total_iva - total_irpf;
    		
    		
    		
    		$(".subtotal").text(subtotal);
    		$(".totaliva").text(total_iva);
    		$(".totalirpf").text(total_irpf);
    		$(".total").text(total);
    		
    		
    	});
    	
    	
    	
    	$("tr.row").live("click" ,function(){
    		//Find on which row you are
    		index = $(this).index();
    		
    		$(this).find("p.precio").text()
    	});
    
    	
    
    });
    Thanks.

  2. #2
    Grüße aus'm Pott gold trophysilver trophybronze trophy
    Pullo's Avatar
    Join Date
    Jun 2007
    Location
    Germany
    Posts
    5,939
    Mentioned
    215 Post(s)
    Tagged
    12 Thread(s)
    Hi,

    The NaN problem is most probably being caused by you trying to do arithmetic operations on a string.
    However, it's a little hard (for me at least) to say, without seeing some HTML.

    Can you post a link to a page where I can see this in action?
    Failing that, could you post enough code that I can save to my PC and recreate your problem with.

  3. #3
    SitePoint Member
    Join Date
    Dec 2013
    Posts
    16
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi, thanks for the reply. Here's the link: http://brunofelicio.com/staging/facturame/

  4. #4
    Web development Company chrisranjana's Avatar
    Join Date
    Jan 2001
    Location
    chennai , tamil nadu , India
    Posts
    705
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by fusionPT View Post
    Hi, thanks for the reply. Here's the link: http://brunofelicio.com/staging/facturame/
    Try changing

    Code:
    $(this).parent().closest("tr").
    to just

    Code:
    $("tr.row").
    Chris, Programmer/Developer,
    www.chrisranjana.com

  5. #5
    SitePoint Member
    Join Date
    Dec 2013
    Posts
    16
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Thanks! That was it. One other thing, I've been reading that I shouldn't use the live method but the "on", but if I substitute the live with the on it doesn't work, any ideas?

    Thanks.

  6. #6
    Grüße aus'm Pott gold trophysilver trophybronze trophy
    Pullo's Avatar
    Join Date
    Jun 2007
    Location
    Germany
    Posts
    5,939
    Mentioned
    215 Post(s)
    Tagged
    12 Thread(s)
    Yeah, .live() was deprecated in an earlier version and .on() was introduced.
    You'll need to update your version of jQuery then. on() will work as expected.

    Out of interest, how much effort do you want to put into tidying up your code?
    There are a couple of areas where you could improve things (e.g. using on instead of live) and I don't mind walking you through it.
    Just let me know if you are interested.

  7. #7
    SitePoint Member
    Join Date
    Dec 2013
    Posts
    16
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi,
    I've got the latest version of jquery, but I think the .on() works a little bit different, right?
    Sure, every input is good, as I am a almost a newbie in this.

    Thanks for your help.

  8. #8
    Grüße aus'm Pott gold trophysilver trophybronze trophy
    Pullo's Avatar
    Join Date
    Jun 2007
    Location
    Germany
    Posts
    5,939
    Mentioned
    215 Post(s)
    Tagged
    12 Thread(s)
    No probs

    Quote Originally Posted by fusionPT View Post
    I've got the latest version of jquery, but I think the .on() works a little bit different, right?
    Yeah, it does:

    Normal syntax:

    Code:
    $(selector).on('click', function() { 
      //... some function ...
    });
    Event delegartion (with optional second parameter):

    Code:
    $(document).on('click', 'selector', function() { 
      //... some function ...
    });
    The second example will be relevant for you, as you are wanting to replace the deprecated live() method.
    In this case, the second, selector parameter tells the handler to listen for the specified event, and when it hears it, check to see if the triggering element for that event matches the second parameter.
    http://learn.jquery.com/events/event-delegation/

    Old:
    Code:
    $('tr').live('click',function() { }):
    New:
    Code:
    $('#myTable').on('click','tr',function() { });

  9. #9
    SitePoint Member
    Join Date
    Dec 2013
    Posts
    16
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    So the
    Code:
    $('#myTable')
    , would be the name of the table that includes the "tr", right?
    I've tried it but doesn't work, must be doing something wrong.

  10. #10
    Grüße aus'm Pott gold trophysilver trophybronze trophy
    Pullo's Avatar
    Join Date
    Jun 2007
    Location
    Germany
    Posts
    5,939
    Mentioned
    215 Post(s)
    Tagged
    12 Thread(s)
    Can you post a link to a demo of it not working?
    I'm sure it's something simple.

  11. #11
    SitePoint Member
    Join Date
    Dec 2013
    Posts
    16
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Got it working, must have misspelled something it now works. Do you think this should be split into functions?

  12. #12
    SitePoint Member
    Join Date
    Dec 2013
    Posts
    16
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    You can take a look at it here: http://brunofelicio.com/staging/facturame/

  13. #13
    Grüße aus'm Pott gold trophysilver trophybronze trophy
    Pullo's Avatar
    Join Date
    Jun 2007
    Location
    Germany
    Posts
    5,939
    Mentioned
    215 Post(s)
    Tagged
    12 Thread(s)
    Absolutely.
    I'm a bit tied up at work right now, but if you make me a demo page then, when I have some time this evening, I can go through and make some suggestions on what you can improve.

    Edit:

    Just saw the link you posted in the previous post. I'll use that.

  14. #14
    SitePoint Member
    Join Date
    Dec 2013
    Posts
    16
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi, thanks.

    Here's the html markup:
    Code:
    <!doctype html>
    
    <head>
      <meta charset="utf-8">
    
      <!-- Always force latest IE rendering engine (even in intranet) & Chrome Frame
           Remove this if you use the .htaccess -->
      <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
    
      <title></title>
      <meta name="description" content="facturame">
      <meta name="author" content="">
    
      <!-- Mobile viewport optimized: j.mp/bplateviewport -->
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
    
      <!-- Place favicon.ico & apple-touch-icon.png in the root of your domain and delete these references -->
      <link rel="shortcut icon" href="/favicon.ico">
      <link rel="apple-touch-icon" href="/apple-touch-icon.png">
    
    
      <!-- CSS: implied media="all" -->
      <link rel="stylesheet" href="css/reset.css">
      <link rel="stylesheet" href="css/style.css">
    
    
      <!-- All JavaScript at the bottom, except for Modernizr which enables HTML5 elements & feature detects -->
      <script src="js/libs/modernizr-1.7.min.js"></script>
    
    </head>
    
    <body>
    
      <div id="container">
      <div id="page-container">
      
      <form id="invoice" method="POST" action="invoice_html.php"><!-- Invoice form -->
      
        <header>
    		
    		<input type="file" name="image">
    		<fieldset>
    			<input type="text" name="invoice" placeholder="Numero de factura" id="inv"/><br />
    			<input type="text" name="date" placeholder="26-01-12"/><br />
    		</fieldset>
        </header>
        
        	<div id="main" role="main">
        	<div id="adress">
        	
        		<fieldset id="from">
        		<h2>Factura de:</h2>
    				<input type="text" name="name" placeholder="Escribe tu nombre"/><br />
    				<input type="text" name="email" placeholder="Escribe tu email"/><br />
    				<input type="text" name="tel" placeholder="Escribe tu teléfono"/><br />
    				<input type="text" name="dir" placeholder="Escribe tu dirección"/>
    			</fieldset>
    		
    			<fieldset id="billto">
    			<h2>Facturar a:</h2>
    				<input type="text" name="cname" placeholder="Escribe tu nombre"/><br />
    				<input type="text" name="cemail" placeholder="Escribe tu e-mail"/><br />
    				<input type="text" name="ctel" placeholder="Escribe tu telefono"/><br />
    				<input type="text" name="cdir" placeholder="Escribe tu dirección"/>
    			</fieldset>
    		
       		</div>	
       		
       		<div id="details">
       			<h2>Detalles</h2>
       				<table id="invoice-table">
       					<thead>
       						<tr>
       							<th>Opciones</th>
       							<th>Descripción</th>
       							<th>Cant.</th>
       							<th>Coste</th>
       							<th>Precio</th>
       							
       						</tr>
       					</thead>
       					<tbody>
       						<tr class="row">
       							<td><a href="#" class="delete-row">Delete</a></td>
       							<td><input type="text" name="description" placeholder="Descripción del articulo" size="40" value="Some description"/></td>
       							<td><input class="cantidad" type="text" name="cost" placeholder="Cant." size="2" value="0"/></td>
       							<td><input class="coste" type="text" name="qty" placeholder="Coste" size="2" value="0"/></td>
       							<td><p class="precio">0</p></td>
       							
       						</tr>
       						<tr>
       							<td><a href="#" class="add-row">Add row</a></td>
       						</tr>
       						
       					</tbody>
       					<tfoot>
       						
       						<tr>
       							<td class="noborder" rowspan="4" colspan="3"></td>
       							<td></td>
       							<td class="subtotal">0</td>
       						</tr>
       						<tr>
       							
       							<td>IVA <input class="iva" type="text" name="tax1" placeholder="IVA" value="21" size="2"/>%</td>
       							<td class="totaliva"></td>
       						</tr>
       						<tr>
       							<td>IRPF<input class="irpf" type="text" name="tax2" placeholder="IRPF" value="21" size="2"/>%</td>
       							<td class="totalirpf"></td>
       						</tr>
       						<tr>
       							<td><strong>TOTAL</strong></td>
       							<td class="total">0</td>
       						</tr>
       					</tfoot>
       				</table>
       		
       		</div>
       		
       		<div id="pay">
       			<h2>Metodo de pago</h2>
       			
       			<textarea name="description" placeholder="Método pago">
       			</textarea>
       		</div>
    
        	</div><!--! end of #main -->
     	
       
        </div> <!--! end of #page-container -->
        <div class="sidebar">
        		<input type="submit" name="submit" id="submit" value="Guardar Factura"/>
        		<input type="submit" name="Enviar" id="enviar" value="Enviar Factura"/>
        	</div><!-- End of sidebar -->
    
       		</form>
       		<div class="clearfix"></div>
       <footer>
    	<p>&#169; brunofelicio.com</p>		
      </footer>
      
      </div> <!--! end of #container -->
      
     
    
      <!-- JavaScript at the bottom for fast page loading -->
    
      <!-- Grab Google CDN's jQuery, with a protocol relative URL; fall back to local if necessary -->
      <script src="js/jquery-1.10.2.min.js"></script>
      <script src="js/app-ck.js"></script>
      <!-- end scripts-->
    
    
      <!--[if lt IE 7 ]>
        <script src="js/libs/dd_belatedpng.js"></script>
        <script>DD_belatedPNG.fix("img, .png_bg"); // Fix any <img> or .png_bg bg-images. Also, please read goo.gl/mZiyb </script>
      <![endif]-->
    
    
      <!-- mathiasbynens.be/notes/async-analytics-snippet Change UA-XXXXX-X to be your site's ID -->
      <script>
        var _gaq=[["_setAccount","UA-XXXXX-X"],["_trackPageview"]];
        (function(d,t){var g=d.createElement(t),s=d.getElementsByTagName(t)[0];g.async=1;
        g.src=("https:"==location.protocol?"//ssl":"//www")+".google-analytics.com/ga.js";
        s.parentNode.insertBefore(g,s)}(document,"script"));
      </script>
    
    </body>
    </html>
    And the app.js:

    Code:
    $(document).ready(function(){
    	var index = 0;
    	
    	//Add row
    	var numRows = 0;
    	$(".add-row").click(function() {
    		numRows++;
    		console.log( "There are: " + numRows + " rows");
    		$(this).parent().parent().before("<tr class='row'><td><a href='#' class='delete-row'>Delete</a></td><td><input type='text' name='description' placeholder='Descripción del articulo' size='40' value=''/></td><td><input class='cantidad' type='text' name='cantidad' placeholder='Cant.' size='2' value=''/></td><td><input class='coste' type='text' name='coste' placeholder='Coste' size='2'/></td><td><p class='precio'>0</p></td></tr></tr>");
    	});
    	
    	//Delete row
    	
    	$("table").on("click",".delete-row", function() {
    		
    		$(this).parent().parent().remove();
    		numRows--;
    		
    		console.log( "There are: " + numRows + " rows");
    		console.log( "array: " + lineTotalArray);
    	});
    		
    
    	//Calculate
    	
    	$("table").on("keyup","input",function() {
    	
    		//Check values in the input boxes
    		//$(this).parent().closest('tr').find(".coste").val();
    		
    		var coste = $(this).parent().closest('.row').find(".coste").val();
    		var cantidad = $(this).parent().closest('.row').find(".cantidad").val();
    		var iva = $(".iva").val();
    		var irpf = $(".irpf").val();
    		
    		//Add Percentage
    		var per_iva = Number("0." + iva);
    		var per_irpf = Number( "0." + irpf);
    		var subtotal = 0;
    		var valueArray = [];
    		var total=0;
    		//Totals
    		
    		var lineTotal = Number(cantidad * coste);
    		$("tr.row").find(".precio").eq(index).text(lineTotal);
    		
    		for (i=0; i<=numRows; i++ ){
    			
    			valueArray[i] = Number($("tr.row").find(".precio").eq(i).text());
    			subtotal += parseInt(valueArray[i]);
    		}
    		
    		var total_iva = (subtotal * per_iva);
    		var total_irpf = (subtotal * per_irpf);
    		
    		
    		total = subtotal + total_iva - total_irpf;
    		
    		
    		
    		$(".subtotal").text(subtotal);
    		$(".totaliva").text(total_iva);
    		$(".totalirpf").text(total_irpf);
    		$(".total").text(total);
    		
    		
    	});
    	
    	
    	
    	$("table").on("click", "tr.row",function(){
    		//Find on which row you are
    		index = $(this).index();
    		console.log('row: '+index);
    		$(this).find("p.precio").text()
    	});
    
    	
    
    });
    Also the link: http://brunofelicio.com/staging/facturame/

    Thanks.

  15. #15
    SitePoint Member
    Join Date
    Dec 2013
    Posts
    16
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I've changed the code a little bit, but the subtotal sum is wrong... all the row sums go into an array, but for some how instead of summing what's the array, it sums every time I add a different number...
    Here's the code. Thanks in advance.

    Code:
    $(document).ready(function(){
    	var index = 0;
    	var valueArray = [];
    	var subtotal = 0;
    	var coste = 0;
    	var cantidad = 0;
    	var iva = 0;
    	var irpf = 0;
    	var total_iva = 0;	
    	var total_irpf = 0;
    	//Add Percentage
    	var per_iva = 0;
    	var per_irpf = 0;
    		
    		
    	var total=0;
    	//Totals
    		
    	
    	
    	
    	//Add row
    	var numRows = 0;
    	$(".add-row").click(function() {
    		numRows++;
    		console.log( "There are: " + numRows + " rows");
    		$(this).parent().parent().before("<tr class='row'><td><a href='#' class='delete-row'>Delete</a></td><td><input type='text' name='description' placeholder='Descripción del articulo' size='40' value=''/></td><td><input class='cantidad' type='text' name='cantidad' placeholder='Cant.' size='2' value=''/></td><td><input class='coste' type='text' name='coste' placeholder='Coste' size='2'/></td><td><p class='precio'>0</p></td></tr></tr>");
    	});
    	
    	//Delete row
    	
    	$("table").on("click",".delete-row", function() {
    		
    		//Removes row
    		
    		$('.row').eq(index).remove();
    		
    		//Removes value from array
    		
    		valueArray.splice(valueArray.indexOf( index ), 1);
    		
    		numRows--;
    		
    		console.log( "There are: " + numRows + " rows");
    		
    	});
    		
    
    	//Calculate
    	
    	$("table").on("keyup","input",function() {
    	
    		//Check values in the input boxes
    		//$(this).parent().closest('tr').find(".coste").val();
    		
    		var coste = $('.row').eq(index).find(".coste").val();
    		var cantidad = $('.row').eq(index).find(".cantidad").val();
    		var iva = $(".iva").val();
    		var irpf = $(".irpf").val();
    		
    		//Add Percentage
    		var per_iva = Number("0." + iva);
    		var per_irpf = Number( "0." + irpf);
    		
    		
    		var total=0;
    		//Totals
    		
    		var lineTotal = Number(cantidad * coste);
    		$("tr.row").find(".precio").eq(index).text(lineTotal);
    		
    		calculateTotals();
    		
    		
    		
    		$(".subtotal").text(subtotal);
    		$(".totaliva").text(total_iva);
    		$(".totalirpf").text(total_irpf);
    		$(".total").text(total);
    		
    		
    	});
    	
    	
    	
    	$("table").on("click", "tr.row",function(){
    		//Find on which row you are
    		index = $(this).index();
    		console.log('row: '+index);
    		$(this).find("p.precio").text()
    	});
    
    	function calculateTotals() {
    		
    		for (i=0; i<=numRows; i++ ){
    			
    			valueArray[i] = Number($("tr.row").find(".precio").eq(i).text());
    			//subtotal = parseInt(valueArray[i]);
    			subtotal = subtotal + parseInt(valueArray[i]);
    			console.log("Array nº: "+valueArray[i]);
    		}
    		
    		console.log("Array: "+valueArray);
    		
    		console.log("Subtotal: "+subtotal);
    		
    		var total_iva = (subtotal * per_iva);
    		var total_irpf = (subtotal * per_irpf);
    		
    		
    		total = subtotal + total_iva - total_irpf;
    	}
    
    });

  16. #16
    Grüße aus'm Pott gold trophysilver trophybronze trophy
    Pullo's Avatar
    Join Date
    Jun 2007
    Location
    Germany
    Posts
    5,939
    Mentioned
    215 Post(s)
    Tagged
    12 Thread(s)
    Hi there,

    So, let's have a look then.

    The first thing I did was cut out all of the HTML apart from the table.
    I also removed the link to the modenizr library, as that was throwing a 404 and removed everything from the $(document).ready() callback.

    Your code does two things:
    1. Dynamically add and remove table rows
    2. Dynamically calculate totals based on input entered.

    I'd like to start by looking at the first point. We can go on to point two in a separate step.

    The first thing that jumps out at me is the large HTML string in the middle of your .add-row click handler.
    This is the same as the first row in your table, so we can just clone that instead and use it as a template:

    Code:
    var template = $("#invoice-table > tbody > tr:first").clone();
    Next, let's remove the console.log statements, as they could break IE 8 and don'`t really serve a purpose.

    console.log( "There are: " + numRows + " rows");
    console.log( "There are: " + numRows + " rows");


    Ok. Now, let's make something happen when you click the "Add row" button.
    This is a link, so we'll pass our event handler a reference to the click event and tell it to prevent the browser's default action, so that the form doesn't submit. This is the problem you were having in your previous post.

    Code:
    $(".add-row").on("click", function(e) {
      console.log("yay");
      e.preventDefault();
    });
    Now let's have it append our template to the bottom of the table.

    Code:
    $(".add-row").on("click", function(e) {
      var newRow = template.clone();
      $("#invoice-table > tbody > tr.row:last").after(newRow);
      e.preventDefault();
    });
    So, that's adding rows taken care of. Now we need to add the remove functionality to the "Delete Button".
    This uses the previously mentioned event delegation.

    Code:
    $("#invoice-table").on("click",".delete-row", function(e) {
      $(this).closest("tr").remove();
      e.preventDefault();
    });
    It might be worth mentioning at this point that you shouldn't be able to remove the remove the first table row.
    Let's check for that:

    Code:
    $("#invoice-table").on("click",".delete-row", function(e) {
      var tableRow = $(this).closest("tr");
      if(tableRow.is(":first-child")){
        alert("Don't be silly!");
      } else {
        tableRow.remove();
      }
      e.preventDefault();
    });
    And there you go. The add remove row functionality has been implemented.

    There's quite a lot to digest here, especially if you're new to jQuery, so have a read of what I wrote and let me know if you have any questions.

    After that we can go on to the second part (updating the total).

    Here's the complete code:

    Code:
    <!doctype html>
      <head>
        <meta charset="utf-8">
        <title>Invoice generator</title> 
        <base href="http://brunofelicio.com/staging/facturame/"/>
        <link rel="stylesheet" href="css/reset.css">
        <link rel="stylesheet" href="css/style.css">
        <style>#template{ display: none; }</style>
      </head>
    
      <body>
        <div id="container">
          <div id="page-container">
            <form id="invoice" method="POST" action="invoice_html.php"><!-- Invoice form -->
              <div id="main" role="main">
                <div id="details">
                  <h2>Detalles</h2>
                  <table id="invoice-table">
                    <thead>
                      <tr>
                        <th>Opciones</th>
                        <th>Descripción</th>
                        <th>Cant.</th>
                        <th>Coste</th>
                        <th>Precio</th>
                      </tr>
                    </thead>
                    <tbody>
                      <tr class="row">
                        <td><a href="#" class="delete-row">Delete</a></td>
                        <td><input type="text" name="description" placeholder="Descripción del articulo" size="40" value="Some description"/></td>
                        <td><input class="cantidad" type="text" name="cost" placeholder="Cant." size="2" value="0"/></td>
                        <td><input class="coste" type="text" name="qty" placeholder="Coste" size="2" value="0"/></td>
                        <td><p class="precio">0</p></td>
                      </tr>
                      <tr>
                        <td><a href="#" class="add-row">Add row</a></td>
                      </tr>
                    </tbody>
                    <tfoot>
    
                      <tr>
                        <td class="noborder" rowspan="4" colspan="3"></td>
                        <td></td>
                        <td class="subtotal">0</td>
                      </tr>
                      <tr>
                        <td>IVA <input class="iva" type="text" name="tax1" placeholder="IVA" value="21" size="2"/>%</td>
                        <td class="totaliva"></td>
                      </tr>
                      <tr>
                        <td>IRPF<input class="irpf" type="text" name="tax2" placeholder="IRPF" value="21" size="2"/>%</td>
                        <td class="totalirpf"></td>
                      </tr>
                      <tr>
                        <td><strong>TOTAL</strong></td>
                        <td class="total">0</td>
                      </tr>
                    </tfoot>
                  </table>
                </div>
              </div><!--! end of #main -->
            </div> <!--! end of #page-container -->
          </form>
        </div> <!--! end of #container -->
    
        <script src="js/jquery-1.10.2.min.js"></script>
        <script>
          var template = $("#invoice-table > tbody > tr:first").clone();
          
          $(".add-row").on("click", function(e) {
            var newRow = template.clone();
            $("#invoice-table > tbody > tr.row:last").after(newRow);
            e.preventDefault();
          });
    
          $("#invoice-table").on("click",".delete-row", function(e) {
            var tableRow = $(this).closest("tr");
            if(tableRow.is(":first-child")){
              alert("Don't be silly!");
            } else {
              tableRow.remove();
            }
            e.preventDefault();
          });
        </script>
      </body>
    </html>

  17. #17
    SitePoint Member
    Join Date
    Dec 2013
    Posts
    16
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi, this is awesome, thanks! It actually makes a lot more sense. I've only seen your reply now, so I've made a couple of changes myself to the code (still need to incorporate your changes). There's one problem now, it adds and removes correctly also the math (the sum of the rows) is also correct, but when I delete a row, I can delete that row's value from the array,but I aslo need to recalculate the all thing, so it outputs the correct value, that is the tricky part (at least for me). I suppose that those calculation should be in a function that I could reuse inside the eliminate handler?

    Here's the code now(still need to incorporate your above changes):
    Code:
    $(document).ready(function(){
    	var index = 0;
    	var valueArray = [];
    	var subtotal = 0;
    	var total = 0;
    	var per_iva = 0;
    	var per_irpf = 0;
    		
    	//Add row
    	var numRows = 1;
    	$(".add-row").click(function() {
    		numRows++;
    		console.log( "There are: " + numRows + " rows");
    		$(this).parent().parent().before("<tr class='row'><td><a href='#' class='delete-row'>Delete</a></td><td><input type='text' name='description' placeholder='Descripción del articulo' size='40' value=''/></td><td><input class='cantidad' type='text' name='cantidad' placeholder='Cant.' size='2' value=''/></td><td><input class='coste' type='text' name='coste' placeholder='Coste' size='2'/></td><td><p class='precio'>0</p></td></tr></tr>");
    	});
    	
    	//Delete row
    	
    	$("table").on("click",".delete-row", function() {
    		
    		//Looks for the table row index
    		
    		//Removes row
    		if ( numRows > 1 ) {
    			numRows--;
    			index = $(this).parent().parent().index('tr.row');
    			$('tr.row').eq(index).remove();
    			//Removes value from array
    		
    			valueArray.splice(valueArray.indexOf( index ), 1);
    		} else {
    			alert("You cannot eliminate the only row you have!");
    		}
    		
    		
    
    		
    		
    		//It has to re-calculate the totals by mayber calling a function...
    		
    		console.log( "Arrays: " + valueArray);
    		console.log( "Subtotal: " + subtotal);
    		
    
    	});
    		
    
    	//Calculate
    	
    	$("table").on("keyup","input",function() {
    	
    		//Check values in the input boxes
    		
    		subtotal = 0;
    		total = 0;
    		var coste = $('.row').eq(index).find(".coste").val();
    		var cantidad = $('.row').eq(index).find(".cantidad").val();
    		var iva = $(".iva").val();
    		var irpf = $(".irpf").val();
    		
    		//Add Percentage
    		var per_iva = Number("0." + iva);
    		var per_irpf = Number( "0." + irpf);
    		
    		//Totals
    		
    		var lineTotal = Number(cantidad * coste);
    		$("tr.row").find(".precio").eq(index).text(lineTotal);
    		
    		for (i=0; i<=numRows; i++ ){
    			
    			valueArray[i] = Number($("tr.row").find(".precio").eq(i).text());
    			subtotal += parseInt(valueArray[i]);
    		}
    		
    		var total_iva = (subtotal * per_iva);
    		var total_irpf = (subtotal * per_irpf);
    		
    		
    		total = subtotal + total_iva - total_irpf;
    		
    		
    		
    		$(".subtotal").text(subtotal);
    		$(".totaliva").text(total_iva);
    		$(".totalirpf").text(total_irpf);
    		$(".total").text(total);
    		
    		
    	});
    	
    	
    	
    	$("table").on("click", "tr.row",function(){
    		//Find on which row you are
    		index = $(this).index();
    		console.log('row: '+index);
    		$(this).find("p.precio").text();
    	});
    
    	
    
    });
    Thanks for helping me out.

  18. #18
    Grüße aus'm Pott gold trophysilver trophybronze trophy
    Pullo's Avatar
    Join Date
    Jun 2007
    Location
    Germany
    Posts
    5,939
    Mentioned
    215 Post(s)
    Tagged
    12 Thread(s)
    Hey, no problem

    I'll have a look at the second part tomorrow.
    Just take the time to read what I did so far and make sure you understand everything.

  19. #19
    Grüße aus'm Pott gold trophysilver trophybronze trophy
    Pullo's Avatar
    Join Date
    Jun 2007
    Location
    Germany
    Posts
    5,939
    Mentioned
    215 Post(s)
    Tagged
    12 Thread(s)
    Hello again,

    So, let's have a look at updating the form total.
    We will need to do this any time the user inputs something into one of our table's input fields (apart from the "Descripción" field).

    Code:
    $("#invoice-table").on("input", function(e) {
      if(e.target.className !== "description"){
        console.log("Yay");
      }
    });
    We are using the oninput event to do this, examining this event to determine which field triggered it and reacting accordingly.
    Note: so that we can reference the field by its class, I have changed the Descripción field's name attribute to a class attribute.

    In the updateTotal function we need to get every row in the table, extract the quantity and cost of the item and update the price:

    Code:
    function updateTotal(){
      $("#invoice-table > tbody > tr.row").each(function(){
        var quantity = Number($(this).find("input.cantidad").val()),
            cost = Number($(this).find("input.coste").val());
        $(this).find("p.precio").text(cost * quantity);
      });
    }
    And we need to update the subtotal:

    Code:
    function updateTotal(){
      var subtotal= 0;
    
      $("#invoice-table > tbody > tr.row").each(function(){
        var quantity = $(this).find("input.cantidad").val(),
            cost = $(this).find("input.coste").val();
            price = +cost * +quantity;
        $(this).find("p.precio").text(price);
        subtotal += price;
      });
    
      $(".subtotal").text(subtotal);
    }
    Note in the above, I've used a unary plus as a synonym for Number().

    All that's left now is the grand total, which is the subtotal plus IVA percent of the subtotal plus IRPF percent of the subtotal.

    Code:
    e.g.
    quantity 2, coste 50
    = price 100
    = subtotal 100
    + 
    iva 21% = 100 *0. 21
    = 21
    +
    irpf 22% = 100 * 0.22
    = 22
    
    = 100 + 21 +22
    I hope I got that right.
    Anyways, here's the implementation:

    Code:
    function updateTotal(){
      var subtotal = 0,
          iva = Number($(".iva").val()) / 100,
          irpf = Number($(".irpf").val()) / 100,
          totaliva,
          totalirpf;
    
      $("#invoice-table > tbody > tr.row").each(function(){
        var quantity = $(this).find("input.cantidad").val(),
            cost = $(this).find("input.coste").val();
            price = +cost * +quantity;
        $(this).find("p.precio").text(price);
        subtotal += price;
      });
    
      totaliva = subtotal*iva;
      totalirpf = subtotal*irpf;
    
      $(".subtotal").text(subtotal);
      $(".totaliva").text(totaliva);
      $(".totalirpf").text(totalirpf);
      $(".total").text(subtotal + totaliva + totalirpf);
    }
    That function is actually doing quite a lot of things and you should probably split it into further functions, but I'll leave that to you

    The final thing to do is to make sure any decimals are rounded to two decimal places, otherwise you'll have prices like "22.259999999999998" which would be silly.
    This will only affect the totaliva, totalirpf and total fields.

    You could write:

    Code:
    totaliva = Math.round((subtotal*iva) * 100) / 100;
    totalirpf = Math.round((subtotal*irpf) * 100) / 100;
    $(".total").text(Math.round((subtotal + totaliva + totalirpf) * 100) / 100);
    but that's ugly.

    Let's stick it in a function:

    Code:
    function roundToTwo(value) {
        return(Math.round(value * 100) / 100);
    }
    
    ...
    
    totaliva = roundToTwo(subtotal*iva);
    totalirpf = roundToTwo(subtotal*irpf);
    $(".total").text(roundToTwo(subtotal + totaliva + totalirpf));
    Hope that all makes sense.
    Here's the complete code:

    Code:
    <!doctype html>
      <head>
        <meta charset="utf-8">
        <title>Invoice generator</title> 
        <base href="http://brunofelicio.com/staging/facturame/"/>
        <link rel="stylesheet" href="css/reset.css">
        <link rel="stylesheet" href="css/style.css">
        <style>#template{ display: none; }</style>
      </head>
    
      <body>
        <div id="container">
          <div id="page-container">
            <form id="invoice" method="POST" action="invoice_html.php"><!-- Invoice form -->
              <div id="main" role="main">
                <div id="details">
                  <h2>Detalles</h2>
                  <table id="invoice-table">
                    <thead>
                      <tr>
                        <th>Opciones</th>
                        <th>Descripción</th>
                        <th>Cant.</th>
                        <th>Coste</th>
                        <th>Precio</th>
                      </tr>
                    </thead>
                    <tbody>
                      <tr class="row">
                        <td><a href="#" class="delete-row">Delete</a></td>
                        <td><input type="text" class="description" placeholder="Descripción del articulo" size="40" value="Some description"/></td>
                        <td><input class="cantidad" type="text" name="cost" placeholder="Cant." size="2" value="0"/></td>
                        <td><input class="coste" type="text" name="qty" placeholder="Coste" size="2" value="0"/></td>
                        <td><p class="precio">0</p></td>
                      </tr>
                      <tr>
                        <td><a href="#" class="add-row">Add row</a></td>
                      </tr>
                    </tbody>
                    <tfoot>
    
                      <tr>
                        <td class="noborder" rowspan="4" colspan="3"></td>
                        <td></td>
                        <td class="subtotal">0</td>
                      </tr>
                      <tr>
                        <td>IVA <input class="iva" type="text" placeholder="IVA" value="21" size="2"/>%</td>
                        <td class="totaliva"></td>
                      </tr>
                      <tr>
                        <td>IRPF<input class="irpf" type="text" placeholder="IRPF" value="21" size="2"/>%</td>
                        <td class="totalirpf"></td>
                      </tr>
                      <tr>
                        <td><strong>TOTAL</strong></td>
                        <td class="total">0</td>
                      </tr>
                    </tfoot>
                  </table>
                </div>
              </div><!--! end of #main -->
            </div> <!--! end of #page-container -->
          </form>
        </div> <!--! end of #container -->
    
        <script src="js/jquery-1.10.2.min.js"></script>
        <script>
          var template = $("#invoice-table > tbody > tr:first").clone();
    
          function roundToTwo(value) {
            return(Math.round(value * 100) / 100);
          }
    
          function updateTotal(){
            var subtotal = 0,
                iva = Number($(".iva").val()) / 100,
                irpf = Number($(".irpf").val()) / 100,
                totaliva,
                totalirpf;
    
            $("#invoice-table > tbody > tr.row").each(function(){
              var quantity = $(this).find("input.cantidad").val(),
                  cost = $(this).find("input.coste").val();
                  price = +cost * +quantity;
              $(this).find("p.precio").text(price);
              subtotal += price;
            });
    
            totaliva = roundToTwo(subtotal*iva);
            totalirpf = roundToTwo(subtotal*irpf);
    
            $(".subtotal").text(subtotal);
            $(".totaliva").text(totaliva);
            $(".totalirpf").text(totalirpf);
            $(".total").text(roundToTwo(subtotal + totaliva + totalirpf));
          }
    
          $(".add-row").on("click", function(e) {
            var newRow = template.clone();
            $("#invoice-table > tbody > tr.row:last").after(newRow);
            e.preventDefault();
          });
    
          $("#invoice-table").on("click",".delete-row", function(e) {
            var tableRow = $(this).closest("tr");
            if(tableRow.is(":first-child")){
              alert("Don't be silly!");
            } else {
              tableRow.remove();
              updateTotal();
            }
            e.preventDefault();
          });
    
          $("#invoice-table").on("input", function(e) {
            if(e.target.className !== "description"){
              updateTotal();
            }
          });
        </script>
      </body>
    </html>

  20. #20
    SitePoint Member
    Join Date
    Dec 2013
    Posts
    16
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    This looks very good, I'll take a look at it later today and let you know. Thanks a lot.

  21. #21
    Grüße aus'm Pott gold trophysilver trophybronze trophy
    Pullo's Avatar
    Join Date
    Jun 2007
    Location
    Germany
    Posts
    5,939
    Mentioned
    215 Post(s)
    Tagged
    12 Thread(s)
    No problems

    Feel free to let me know if you have any questions, or would like anything else explained.

  22. #22
    SitePoint Member
    Join Date
    Dec 2013
    Posts
    16
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Every thing works great, and with less code. The only thing that didn't quite worked was the way it didn't allow the user to delete the first row. The way it was you could never delete the first row even if it wasn't the only row, I resolved this by adding a variable that counts the numbers of rows and checks if it's the only one or not.

    Now it's time to send it trough the DomPDF and format the invoice pdf for download. Thanks for the help, I'll let you know if I ran into any trouble (most likely )

    Here's the code:
    Code:
    $(document).ready(function(){
    
    	//HTML row html template
    	var numRows = 1;
    	var template = $("#invoice-table > tbody > tr:first").clone();
    		
    	//Add row
    	
    	$(".add-row").on("click", function(e) {
    		numRows++;
    		var newRow = template.clone();
    		$("#invoice-table > tbody > tr.row:last").after(newRow);
    		e.preventDefault();
    		
    	});
    	
    	//Delete row
    	
    	$("#invoice-table").on("click",".delete-row", function(e) {
    		
    		var tableRow = $(this).closest("tr");
    		
    
    		if(numRows > 1){
    			numRows--;
    			tableRow.remove();
    			updateTotal();
    			
    		} else {
    			alert("You can't delete the only row!");
    		}
    		e.preventDefault();
    
    	});
    		
    	//Calculate
    	
    	$("#invoice-table").on("input", function(e) {
    	
    		if(e.target.className !== "description") {
    			updateTotal();
    		}
    					
    	});
    	
    	//Update total function
    	
    	function updateTotal() {
    		var subtotal = 0,
    
    			iva = Number($(".iva").val()) / 100,
    			irpf = Number($(".irpf").val()) / 100,
    			totaliva,
    			totalirpf;
    			
    		//Loop
    		  $("#invoice-table > tbody > tr.row").each(function(){
              var quantity = $(this).find("input.cantidad").val(),
                  cost = $(this).find("input.coste").val();
                  price = +cost * +quantity;
              $(this).find("p.precio").text(price);
              subtotal += price;
            });
    		
    		totaliva = roundToTwo(subtotal*iva);
    		totalirpf = roundToTwo(subtotal*irpf);
    		total = subtotal + totaliva - totalirpf;
    		
    		
    		
    		$(".subtotal").text(subtotal);
    		$(".totaliva").text(totaliva);
    		$(".totalirpf").text(totalirpf);
    		$(".total").text(roundToTwo(total));
    
    	}
    	
    	
    	//Round function
    	function roundToTwo(value) {
            return(Math.round(value * 100) / 100);
          }
    
    });
    Thanks!

  23. #23
    Grüße aus'm Pott gold trophysilver trophybronze trophy
    Pullo's Avatar
    Join Date
    Jun 2007
    Location
    Germany
    Posts
    5,939
    Mentioned
    215 Post(s)
    Tagged
    12 Thread(s)
    Hi,

    I'm glad it's working.

    Just to be clear - you don't need the $(document).ready(function(){ ... }); if you place your JS at the end of the page (ideally before the closing </body> tag).

    Also, I had included code to prevent deletion of the last row.
    Just use the :first-child filter, no need to count the rows

    Code:
    $("#invoice-table").on("click",".delete-row", function(e) {
      var tableRow = $(this).closest("tr");
      if(tableRow.is(":first-child")){
        alert("Don't be silly!");
      } else {
        tableRow.remove();
        updateTotal();
      }
      e.preventDefault();
    });
    HTH

  24. #24
    SitePoint Member
    Join Date
    Dec 2013
    Posts
    16
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hi, now that I have the javascript functioning I've added the domPDF and am trying to render the html to PDF. I have it working but the rows that I duplicate in the html don't appear. I think I have to copy the html and put it into a variable so that the PHP can use it. Am I right? How would I be able to do that?

    Here's the current link: http://brunofelicio.com/staging/facturame/

    Thanks a lot.

  25. #25
    Grüße aus'm Pott gold trophysilver trophybronze trophy
    Pullo's Avatar
    Join Date
    Jun 2007
    Location
    Germany
    Posts
    5,939
    Mentioned
    215 Post(s)
    Tagged
    12 Thread(s)
    Hi,

    I'm afraid I don't quite understand your question.

    You are using some PHP library to generate PDFs and you want to know how to use JS to send it a bunch of HTML (from which it should then render a PDF).

    Is that correct?


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •