Franz Moser: Preisberechnung mit JavaScript (dynamisches Formular)

Hallo zusammen,
ich habe ein Problem, bei dem ich mir nun schon ewig den Kopf zerbreche und nicht mehr weiter komme. Ich habe ein Bestellformular, welches dynamisch mit Daten aus einer MySQL-Datenbank versorgt wird - dabei ist eine Zeile = ein Produkt d.h. die Anzahl der Zeilen (=Produkte) variiert. Ich hab mir deshalb überlegt, die Felder wie ein Array zu benennen, siehe den PHP-Code:

  
 //Teil aus einer while-Schleife der Datenbankausgabe  
 $produktzeile.= "  
 <tr class=\"ergebniszeile\">  
 <td><input type='text' name='menge_bestellt[".$i."]' maxlength='5' size='3' value='".$differenz."' onChange='javascript:aktKosten(".$grundpreis.",".$ i.");' /> ".$be."</td>  
 <td>".$hinweis.$warnung." <b>".$lausgabe['name']."</b> (".$lausgabe['artikelnr'].") </td>  
 <td>".$produktbestand." / ".$sollbestand." Stück</td>  
 <td>".$produktverfall."</td>  
 <td><input type='text' name='preis[".$i."]' value='".$preis[$i]."' disabled='disabled' style='text-align:right;'  
 onChange='javascript:aktGesKosten();' size='5' /> ".$waehrung."</td>  
 </tr>";

Im Endeffekt wird also in jeder Zeile $i um 1 erhöht, damit sich der Index des Arrays erhöht. In jeder Zeile gibt es ein INPUT-Feld mit der Menge (wird automatisch mit einem Mengenvorschlag gefüllt) und ein INPUT-Feld, welches disabled ist und wo der Preis für die bestimmte Menge des jeweiligen Produkts ausgegeben wird. Standardmäßig wird das Preis-Input-Feld von PHP befüllt. Nun habe ich - bisher leider verzweifelt - versucht, ein JavaScript zu schreiben, welches wenn die Menge vom Benutzer geändert wird auch den Preis in der jeweiligen Zeile am anpasst. NUR LEIDER FUNKTIONIERT DAS JAVASCRIPT NICHT - KEINE AHNUNG WARUM??

 function aktKosten(grundpreis,i) {  
 this.preis[i].value = grundpreis*this.menge_bestellt[i].value;  
  
 }

Außerdem ist am Ende der Tabelle unten ein weiteres disabled INPUT-Feld, das die Gesamtbestellkosten ausgibt, d.h. die Werte in den Preisfeldern der jeweiligen Zeilen addiert. Das klappt mit PHP auch ganz gut:

$gesamtkosten=array_sum($preis);

Ich hab mir das im Prinzip so gedacht: Wenn der Benutzer die Menge ändert, wird automatisch der Preis in der Zeile berechnet ---- und wenn das Preisfeld dann (automatisch) geändert wird, werden die Gesamtkosten auch neu berechnet und ausgegeben.

<td><input type='text' name='gesamtkosten' value='<?php echo $gesamtkosten; ?>' disabled='disabled' style='text-align:right;' size='5' /> <?php echo $waehrung; ?></td></tr>

Die zugehörige angedachte JS-Funktion

 function aktGesKosten() {  
 geskost = 0.00;  
 for (int i=0; i < this.preis.length; i++ ) {  
 geskost += this.preis[i];  
 }  
 this.gesamtkosten.value = geskost;  
 }  

FAZIT: Leider funktionieren beide JS-Funktionen nicht - wer hat hier einen Tipp auf Lager und kann mir helfen? Bitteeee undefined

  1. Hallo,

    eine Bitte vorab: Du stellst selbst schon fest, dass dein Problem in erster Linie ein Javascript-Problem ist. Warum machst du es potentiellen Helfern dann komplizierter als nötig, indem du den PHP-Code postest, der das JS erzeugt?

    <input type='text' name='preis[".$i."]' value='".$preis[$i]."' disabled='disabled' style='text-align:right;' onChange='javascript:aktGesKosten();' size='5' />

    Erstens heißt der Eventhandler onchange (und nicht onChange), aber HTML ist da großzügig. Zweitens ist das Label "javascript:" sinnlos und überflüssig.

    function aktKosten(grundpreis,i) {

    this.preis[i].value = grundpreis*this.menge_bestellt[i].value;
    }

      
    Diese Funktion wird durch den Eventhandler des input-Elements aufgerufen. Was repräsentiert "this" also in diesem Kontext? - Genau, das input-Elementobjekt selbst. Das hat zwar eine value-Eigenschaft, aber keine Eigenschaft namens menge\_bestellt, die sich auch noch wie ein Array indizieren ließe. Mach dir ruhig eine Skizze über die Beziehungen der Objekte untereinander.  
      
    Außerdem lässt du dich verleiten, die Javascript-Objekte, die den input-Elementen entsprechen, als Arrays anzusprechen, nur weil du ihnen Namen gegeben hast, die eckige Klammern enthalten. Das ist falsch. Dein input-Element hat beispielsweise den Namen "preis[3]", das ist kein Array, in dem du das Element mit dem Index 3 adressieren kannst!  
      
    
    > ~~~javascript
    
     function aktGesKosten() {  
    
    >  geskost = 0.00;  
    >  for (int i=0; i < this.preis.length; i++ ) {  
    >  geskost += this.preis[i];  
    >  }  
    >  this.gesamtkosten.value = geskost;  
    >  }
    
    

    Wo oder in welchem Kontext wird diese Funktion aufgerufen? Vermutlich hat this auch hier nicht den Wert, den du erwartest.

    So long,
     Martin

    --
    Wer morgens zerknittert aufsteht, hat den ganzen Tag Gelegenheit, sich zu entfalten.
    Selfcode: fo:) ch:{ rl:| br:< n4:( ie:| mo:| va:) de:] zu:) fl:{ ss:) ls:µ js:(
    1. eine Bitte vorab: Du stellst selbst schon fest, dass dein Problem in erster Linie ein Javascript-Problem ist. Warum machst du es potentiellen Helfern dann komplizierter als nötig, indem du den PHP-Code postest, der das JS erzeugt?

      --> Damit wollte ich den gesamten Code posten, um z.B. Probleme zu erkennen wie, dass ich das gar nicht in ein Array schreibe...

      Diese Funktion wird durch den Eventhandler des input-Elements aufgerufen. Was repräsentiert "this" also in diesem Kontext? - Genau, das input-Elementobjekt selbst. Das hat zwar eine value-Eigenschaft, aber keine Eigenschaft namens menge_bestellt, die sich auch noch wie ein Array indizieren ließe. Mach dir ruhig eine Skizze über die Beziehungen der Objekte untereinander.

      --> das this. habe ich eingefügt, um die Felder in dem Formular auf dieser Seite anzusprechen - aber ich merk schon, das ist vermutlich sinnlos und ich kann es weglassen^^ :-)

      Außerdem lässt du dich verleiten, die Javascript-Objekte, die den input-Elementen entsprechen, als Arrays anzusprechen, nur weil du ihnen Namen gegeben hast, die eckige Klammern enthalten. Das ist falsch. Dein input-Element hat beispielsweise den Namen "preis[3]", das ist kein Array, in dem du das Element mit dem Index 3 adressieren kannst!

      --> wie kann ich dann arrays erzeugen? ich will ja dann das Formualar absenden und die Daten wiederrum dynamisch, je nach dem wie viele Zeilen es sind, in eine Datenbank schreiben...

      Wo oder in welchem Kontext wird diese Funktion aufgerufen? Vermutlich hat this auch hier nicht den Wert, den du erwartest.

      --> Die Funktion steht im Kopf der PHP Seite, d.h. im HTML-Header-Tag, und wird ja von dem einzigen Formular auf dieser Seite aufgerufen.

      Danke schonmal für Eure antworten!!!
      Ich werde heute eure Tipps ausprobieren!

      Eine weitere Frage noch: Stimmt es, dass disabled-Felder auch von JS nicht geändert werden können?

      Viele Grüße
      Franz

      1. Mahlzeit Franz Moser,

        --> das this. habe ich eingefügt, um die Felder in dem Formular auf dieser Seite anzusprechen - aber ich merk schon, das ist vermutlich sinnlos und ich kann es weglassen^^ :-)

        Sinnlos ist nur, wenn Du http://de.selfhtml.org/http://bbsi/selfhtml/javascript/sprache/objekte.htm#this@title=this benutzt, ohne zu wissen, was Du da eigentlich machst ... :-)

        --> wie kann ich dann arrays erzeugen?

        Wo genau willst Du sie erzeugen? Indem Du Formularelemente mit z.B. "preis[1]", "preis[2]", "preis[3]" benennst, erzeugt Dein *verarbeitendes Skript* auf dem *Server* in der Regel entsprechende Arrays. Das hat aber mit Javascript nichts zu tun. Dort handelt es sich um einzelne Elemente, die nur einen etwas "ungewöhnlichen" Namen tragen.

        MfG,
        EKKi

        --
        sh:( fo:| ch:? rl:( br:> n4:~ ie:% mo:} va:) de:] zu:) fl:{ ss:) ls:& js:|
      2. Hi,

        eine Bitte vorab: Du stellst selbst schon fest, dass dein Problem in erster Linie ein Javascript-Problem ist. Warum machst du es potentiellen Helfern dann komplizierter als nötig, indem du den PHP-Code postest, der das JS erzeugt?
        --> Damit wollte ich den gesamten Code posten, um z.B. Probleme zu erkennen wie, dass ich das gar nicht in ein Array schreibe...

        ja, gut gedacht; besser wäre trotzdem gewesen, den Quellcode so zu zeigen, wie er im Browser ankommt.

        Diese Funktion wird durch den Eventhandler des input-Elements aufgerufen. Was repräsentiert "this" also in diesem Kontext? - Genau, das input-Elementobjekt selbst.
        --> das this. habe ich eingefügt, um die Felder in dem Formular auf dieser Seite anzusprechen - aber ich merk schon, das ist vermutlich sinnlos und ich kann es weglassen^^ :-)

        Neineinein, weglassen nicht. Es ist ja durchaus praktisch zu verwenden. Wie gesagt, this ist hier eine Referenz auf das Formularelement selbst, in dessen Kontext es aufgerufen wird. Ein Formularelement hat aber seinerseits wieder eine Eigenschaft namens form, die auf das übergeordnete Formular verweist. Damit kommt man dann an die restlichen Formularelemente dran.

        Außerdem lässt du dich verleiten, die Javascript-Objekte, die den input-Elementen entsprechen, als Arrays anzusprechen, nur weil du ihnen Namen gegeben hast, die eckige Klammern enthalten. Das ist falsch. Dein input-Element hat beispielsweise den Namen "preis[3]", das ist kein Array, in dem du das Element mit dem Index 3 adressieren kannst!
        --> wie kann ich dann arrays erzeugen? ich will ja dann das Formualar absenden und die Daten wiederrum dynamisch, je nach dem wie viele Zeilen es sind, in eine Datenbank schreiben...

        Du bist in der unangenehmen Situation, dass du die Daten einmal mit Javascript und einmal mit PHP auswerten willst, und die beiden Sprachen verhalten sich da unterschiedlich. Betrachten wir ein Beispiel.

        <form name="colorselect" method="get" action="">  
         <input type="checkbox" name="color[]" value="red"   /> Rot  
         <input type="checkbox" name="color[]" value="green" /> Grün  
         <input type="checkbox" name="color[]" value="blue"  /> Blau  
         <input type="submit" value="Absenden" />  
        </form>
        

        Wird dieses Formular abgesendet und zur Auswertung an ein PHP-Script übergeben, dann erzeugt PHP aufgrund der Array-Klammern an den Feldnamen automatisch ein Array mit dem Namen color und bis zu drei Elementen.

        Mit Javascript greifen wir "normalerweise" über formularreferenz.feldname auf eines der Felder zu. Da der Feldname hier aber Sonderzeichen enthält, die als Name einer Eigenschaft in einem JS-Obkekt nicht erlaubt sind, müssen wir eine alternative Form der Adressierung verwenden, nämlich formularreferenz.elements["feldname"].
        Wenn mehrere Elemente mit dem gleichen Namen in einem Formular vorkommen, macht Javascript sogar automatisch ein Array daraus, so dass wir die drei Checkbox-Elementobjekte hier mit
         document.forms.colorsel.elements["color[]"][n]
        ansprechen können, wobei n der Index (0,1,2) des Feldes ist.

        Wenn wir jetzt noch das Pseudoojekt this wieder ins Spiel bringen, können wir das Formular selbst etwas einfacher adressieren. Angenommen, die Checkbox-Felder hätten noch einen onclick-Handler, dann wäre innerhalb der Eventhandler-Funktion this wieder ein Alias für das Checkbox-Objekt selbst, und mit
         this.form.elements["color[]"][n]
        können wir die Nachbarelemente adressieren.

        So, das war jetzt viel auf einmal. Aber das sollte erstmal genug Material sein, mit dem du deine Struktur mal etwas "auf Vordermann" bringen kannst.

        --> Die Funktion steht im Kopf der PHP Seite, d.h. im HTML-Header-Tag, und wird ja von dem einzigen Formular auf dieser Seite aufgerufen.

        Du meinst nicht Header, sondern head, und im head-Tag steht sie ganz gewiss nicht, höchstens im head-Element. Aber das ist nicht relevant, deswegen fragte ich ganz bewusst: Wie und in welchem Element-Kontext wird sie aufgerufen?

        Eine weitere Frage noch: Stimmt es, dass disabled-Felder auch von JS nicht geändert werden können?

        Nein.

        So long,
         Martin

        --
        Schon gewusst, dass Aftershave trotz des Namens eigentlich eher fürs Gesicht gedacht ist?
        Selfcode: fo:) ch:{ rl:| br:< n4:( ie:| mo:| va:) de:] zu:) fl:{ ss:) ls:µ js:(
  2. Hallo,

    1. nimm PHPs alternative Syntax
    2. teste mit HTML und JS, wie Martin schrieb
    3. nimm die Fehlerkonsole von FF
    4. bau dir eine kleine Testdatei, in der du _nur_ dein _eines_ Problem testest (wusste garnicht dass "testest" so nett aussieht (;-)).

    Fazit: das sind vier Tipps, die in einem münden: nur ein Problem auf einmal testen.

    Gruß

    jobo

  3. @@Franz Moser:

    nuqneH

    this.preis[i].value = grundpreis*this.menge_bestellt[i].value;

    Vorsicht! Was du aus Eingabefeldern ausliest, sind Strings!

    JavaScript wandelt ggfs. selbstständig Typen um. Aber spätestens, wenn du nicht mit *, sondern mit + verknüpfst, rennst du ins offene Messer.

    Besser Strings selbst mit parseFloat() bzw. parseInt() in numerische Werte umwandeln.

    Qapla'

    --
    Gut sein ist edel. Andere lehren, gut zu sein, ist noch edler. Und einfacher.
    (Mark Twain)
  4. Bounjoun Franz Moser,

    Zusätzlich zu den Einwänden der Kollegen, würde ich nicht PHP rechnen lassen, sondern JavaScript. PHP brauchst Du dann für die weitere Bearbeitung Deines Bestellformulars nach Belieben, doch alles was der User im Formular sehen will, sollte möglichst in Echtzeit stattfinden - daher JavaScript.

    Beschäftige mit auch mit den Funktionen:
    isNaN()-> was passiert, wenn der Besucher einen Buchstaben statt eine Zahl eingbit,
    Number(),
    toFixed(2) -> irgendwann wirst Du auf zwei Nachkommastellen vielleicht kaufmännisch runden müssen.

    Bestücke Deine Tabellenzellen/Inputfelder mit ID und liest sie darüber aus - oder über andere Dom-Methoden (parentNode, firstChild). Hier vereinfacht und als Beispiel eine Funktion, die ich mal für einen Rechnungschreibprogrämmchen[1] geschrieben habe:

    function calcAll() {  
        var vat = document.getElementById('mwst').value;  
        var articleNum = document.getElementById('anzahl').value;  
        var articlePrice = document.getElementById('preis').value;  
        if(!isNaN(Number(articleNum)) && !isNaN(Number(articlePrice))) {  
            var articleTotal = articleNum * articlePrice;  
            document.getElementById('total').value = Number(articleTotal).toFixed(2);  
            calcVat(vat);  // hier geht's weiter zur MwSt.-Errechnung  
        }  
    }
    

    [1] Das ganze Programm darf ich Dir nicht zeigen, da ich es verkauft habe inkl. Rechte.

    Adiou.

    --
    Ich bin eigentlich ganz anders, aber ich komme so selten dazu. - Ödön von Horwáth
    Ist Rudi Carrell Gott? Oder George Harrison Ford?
    1. @@Jean-Max:

      nuqneH

      Ist Rudi Carrell Gott?

      Nö, aber bei ihm. Reißt da Witze am laufenden Band.

      Oder George Harrison Ford?

      Ja, isser. Zu seinem alten Kumpel Jones. Äh, John.

      Qapla'

      --
      Gut sein ist edel. Andere lehren, gut zu sein, ist noch edler. Und einfacher.
      (Mark Twain)
      1. Bounjoun Gunnar Bittersmann,

        Ist Rudi Carrell Gott?
        Nö, aber bei ihm. Reißt da Witze am laufenden Band.
        Oder George Harrison Ford?
        Ja, isser. Zu seinem alten Kumpel Jones. Äh, John.

        Oder Henry. Wobei die Schleife des (Fließ-)bands geschlossen wäre.

        Adiou.

        --
        Ich bin eigentlich ganz anders, aber ich komme so selten dazu. - Ödön von Horwáth
        Ist Rudi Carrell Gott? Oder George Harrison Ford?
    2. Zusätzlich zu den Einwänden der Kollegen, würde ich nicht PHP rechnen lassen, sondern JavaScript. PHP brauchst Du dann für die weitere Bearbeitung Deines Bestellformulars nach Belieben, doch alles was der User im Formular sehen will, sollte möglichst in Echtzeit stattfinden - daher JavaScript.

      Vielleicht hab ichs nicht ganz einfach ausgedrückt :-) Die erste Berechnung, d.h. während die Seite erzeugt wird, erfolgt mit PHP. Es werden bestimmte Bestellmengen vorgeschlagen und der Preis für die jeweilige Menge ausgegeben.
      Erst wenn der Benutzer die Menge ändert, soll der Preis aktualisiert werden :-)

      Beschäftige mit auch mit den Funktionen:
      isNaN()-> was passiert, wenn der Besucher einen Buchstaben statt eine Zahl eingbit,
      Number(),
      toFixed(2) -> irgendwann wirst Du auf zwei Nachkommastellen vielleicht kaufmännisch runden müssen.

      --> Danke, ein guter Tipp :-)

      Danke schonmal für eure Super tipps!!

  5. So, nun nochmal der aktuelle Stand, hab das Skript ein wenig verändert und ich weiß jetzt, dass es daran liegt, dass ich das richtige Feld nicht ansprechen kann.

    Teil des HTML-Quellcode der erzeugten index.php:

      
    <form method="post" action="save.php?lieferanten_bestellung_neu" name="bestform"><table>  
    <tr class="ergebniszeile">  
    <td><input type='text' name='menge_bestellt[6]' maxlength='5' size='3' value='63' onchange='kostenneu(0.19,6)' /> Stück</td>  
    <td> <b>Fingertip für Absauger</b> (P0014) </td>  
    <td>37 / 100 Stück</td>  
    <td>2013-05</td>  
    <td><input type='text' name='preis[6]' value='11.97' style='text-align:right;' onChange='javascript:aktGesKosten();' size='5' readonly='readonly' /> Euro</td>  
    </tr></table></form>
    

    Das JS habe ich jetzt im head-Tag (natürlich ebenfalls aus der index.php)
    <script type="text/javascript" src="include/bestellkosten.js"></script>

    So und nun die teils funktionierende bestellkosten.js:

    function kostenneu(grundpreis,i) {  
    	window.alert("Funktion kostenneu erfolgreich gestartet! \n\n --> Index: "+i+"\n Grundpreis: "+grundpreis);  
    	mengenfeld = "menge_bestellt["+i+"]";  
    	window.alert(mengenfeld);  
    	wert_mengenfeld = document.getElementById(mengenfeld).value;  
    	window.alert(wert_mengenfeld);  
    }
    

    1. und 2. Alertfenster erscheint ohne Probleme, also das JS wird ausgeführt. In der darauf folgenden Zeile, in der der Wert ausgelesen werden soll , bricht aber das JS ab und es kommt kein 3. Alert-Fenster.

    --> Wie kann ich das Feld richtig ansprechen?

    DANKE & Viele Grüße

    Alex

    1. Mahlzeit Franz Moser,

      <input type='text' name='menge_bestellt[6]' maxlength='5' size='3' value='63' onchange='kostenneu(0.19,6)' />

      function kostenneu(grundpreis,i) {

      window.alert("Funktion kostenneu erfolgreich gestartet! \n\n --> Index: "+i+"\n Grundpreis: "+grundpreis);
      mengenfeld = "menge_bestellt["+i+"]";
      window.alert(mengenfeld);
      wert_mengenfeld = document.getElementById(mengenfeld).value;
      window.alert(wert_mengenfeld);
      }

        
      Warum so kompliziert? Wenn Du <http://de.selfhtml.org/http://bbsi/selfhtml/javascript/sprache/objekte.htm#this@title=this> sinnvoll einsetzt, kannst Du die Funktion z.B. folgendermaßen aufrufen:  
        
      `<input type='text' name='menge_bestellt[6]' maxlength='5' size='3' value='63' onchange='kostenneu(this, 0.19)' />`{:.language-html}  
        
      Dann muss die Funktionsdeklaration natürlich entsprechend aussehen:  
        
      ~~~javascript
      function kostenneu(element, grundpreis) {  
      	window.alert("Funktion kostenneu erfolgreich gestartet! \n\n --> Element: " + element + "\n Grundpreis: " + grundpreis);  
      	wert_mengenfeld = element.value;  
      	window.alert(wert_mengenfeld);  
      }
      

      MfG,
      EKKi

      --
      sh:( fo:| ch:? rl:( br:> n4:~ ie:% mo:} va:) de:] zu:) fl:{ ss:) ls:& js:|
      1. Hallo,

        viel interessanter als irgendwelche Rafinesse finde ich, dass der OP hier

        <input type='text' name='menge_bestellt[6]' maxlength='5' size='3' value='63' onchange='kostenneu(0.19,6)' />

        einen Namen vergibt, aber dann im Javascript

        mengenfeld = "menge\_bestellt["+i+"]";  
        window.alert(mengenfeld);  
        wert\_mengenfeld = document.getElementById(mengenfeld).value;  
        

        nach einer ID sucht. Logisch, dass das in die Binsen geht.

        Ciao,
         Martin

        --
        Zwei Mäuse treiben's miteinander. Sagt der Mäuserich: "Hoffentlich ist nicht wieder alles für die Katz."
        Selfcode: fo:) ch:{ rl:| br:< n4:( ie:| mo:| va:) de:] zu:) fl:{ ss:) ls:µ js:(
        1. Mahlzeit Der Martin,

          viel interessanter als irgendwelche Rafinesse finde ich, dass der OP hier

          [...]

          einen Namen vergibt, aber dann im Javascript

          [...]

          nach einer ID sucht. Logisch, dass das in die Binsen geht.

          Klar - aber das hat er bereits selbst bemerkt (deshalb bin ich darauf schon gar nicht mehr eingegangen) ... :-)

          MfG,
          EKKi

          --
          sh:( fo:| ch:? rl:( br:> n4:~ ie:% mo:} va:) de:] zu:) fl:{ ss:) ls:& js:|
  6. SO - UND DAS HAT GEFEHLT:

    das id-Attribut für das input-feld :-D

    Und damit hat sich auch das Problem mit dem Array erledigt! :-)

    Danke für eure Hilfe!!!