j4nk3y: preventDefault() nach Ajax request

Guten AAbend zusammen,

Ich hätte da mal wieder einen Punkt wo ich nicht weiter komme.

Und zwar versuche ich einen Formular Button, der aus einem Ajax request kommt, mit event.preventDefault(); zu behandeln. Das sieht etwa folgendermaßen aus.

a.php

$arr = array(a,b,c,d,e,f);
$response = array();
foreach($arr as $value)
{
	$markUp = '';
	if($value == 'a' OR $value == 'c')
	{
		$markUp = $markUp.'<form action="b.php" method="post">
					<input class="button" id="submit_'.$value.'" type="submit" onclick="submit('.$value.')"/>
				<form>';

		$isButton = 'TRUE';
	}
	else
	{
		$isButton = 'FALSE';
	}

	$response[$value]['markUp'] = $markUp;
	$response[$value]['isButton'] = $isButton;
}

echo json_encode($response);

Nun der Ajax request:

function list()
{
	var hr = new XMLHttpRequest();
	var url = "a.php";
	
	hr.onreadystatechange = function()
	{
		if (hr.readyState == 4 && hr.status == 200)
		{
			var div = document.getElementById("content_wrapper");
			
			var response = JSON.parse(hr.responseText);
			
			for(var key in response){
					
				div.innerHTML += response[key]['markUp'];	
				
				if(response[key]['isButton'] == 'TRUE'){
					document.getElementById("submit_"+key).addEventListener("click", function(event){
		event.preventDefault()
	});
					
				}
			}
		}
	}
	
	hr.open("POST", url, true)
	hr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
	hr.send();
}

Ja, so sieht es gerade aus, wenn ich kein Fehler beim tippen gemacht habe. Habe ich einen gar schrecklichen Denkfehler?

Jedenfalls ist das Element mit der ID vorhanden wenn document.getElementById("submit_"+key).addEventListener("click", function(event) zur Tat schreitet, da kommt aufjedenfall kein Fehler zurück.

Vielen Dank schonmal für eure Hilfe.

Gruß Jo

  1. '<input class="button" id="submit_'.$value.'" type="submit" onclick="submit('.$value.')"/>'
    
    document.getElementById("submit_"+key).addEventListener("click",  function(event){
      event.preventDefault()
    });
    

    Ja, so sieht es gerade aus, wenn ich kein Fehler beim tippen gemacht habe. Habe ich einen gar schrecklichen Denkfehler?

    Ganz schönes Gefrickel. Ich habe die zwei in Konflikt stehenden Codestellen mal direkt gegenüber gestellt. Die Default-Aktion des Buttons wäre das Absenden des Formulars. Zusätzlich zu dieser Default-Aktion verpasst du dem Button aber auch noch die benutzerdefinierte Aktion onclick="submit('.$value.')". Ich vermute mal, dass die Funktion submit auch das Formular absenden wird. Das heißt, du unterdrückst vielleicht erfolgreich die Default-Aktion, aber nicht deine benutzerdefinierte Funktion, die im Prinzip das gleiche macht.

    Vorschlag zur Güte: Räume erstmal auf. JavaScript mit PHP zu generieren ist eine ganz schlechte Idee, das macht es unglaublich schwierig vorherzusehen, wie das Programm reagiert. Und Event-Handler im HTML zu notieren, ist aus ähnlichen Gründen ein stilistisches No-Go. Besser wäre du registrierst alle Event-Handler aus deinem JavaScript heraus und benutzt Event-Delegation, um mit den dynamisch nachgeladenen Buttons zu interagieren. In der Forensuche dürftest du auf einige gute Beiträge zu dem Thema stoßen, sehr zu empfehlen sind zum Beispiel die von @Orlok.

    1. Morgen,

      Das heißt, du unterdrückst vielleicht erfolgreich die Default-Aktion, aber nicht deine benutzerdefinierte Funktion, die im Prinzip das gleiche macht.

      Leider wird die Default- Aktion nicht unterdrückt und das Formular regulär abgeschickt. Sonst hätte ich ja kein Problem und müsste nicht Nachfragen.

      Vorschlag zur Güte: Räume erstmal auf. JavaScript mit PHP zu generieren ist eine ganz schlechte Idee, das macht es unglaublich schwierig vorherzusehen, wie das Programm reagiert. Und Event-Handler im HTML zu notieren, ist aus ähnlichen Gründen ein stilistisches No-Go. Besser wäre du registrierst alle Event-Handler aus deinem JavaScript heraus und benutzt Event-Delegation, um mit den dynamisch nachgeladenen Buttons zu interagieren.

      Also in etwa so?

      a.php

      $arr = array(a,b,c,d,e,f);
      $response = array();
      foreach($arr as $value)
      {
      	$markUp = '';
      	if($value == 'a' OR $value == 'c')
      	{
      		$markUp = $markUp.'<form action="b.php" method="post">
      					<input class="button" id="submit_'.$value.'" type="submit"/>
      				<form>';
      
      		$isButton = 'TRUE';
      	}
      	else
      	{
      		$isButton = 'FALSE';
      	}
      
      	$response[$value]['markUp'] = $markUp;
      	$response[$value]['isButton'] = $isButton;
      }
      
      echo json_encode($response);
      

      Funktionsaufruf im Input entfernt*

      Nun der Ajax request:

      function list()
      {
      	var hr = new XMLHttpRequest();
      	var url = "a.php";
      	
      	hr.onreadystatechange = function()
      	{
      		if (hr.readyState == 4 && hr.status == 200)
      		{
      			var div = document.getElementById("content_wrapper");
      			
      			var response = JSON.parse(hr.responseText);
      			
      			for(var key in response){
      					
      				div.innerHTML += response[key]['markUp'];	
      				
      			}
      			click();
      		}
      	}
      	
      	hr.open("POST", url, true)
      	hr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
      	hr.send();
      }
      

      Was ich nach kurzer Recherche gefunden Hab.

      funktion buttonClick (e) {
         
         var e = e || window.event;
         var target = e.target || e.srcElement;
      
         var elementName = target.nodeName,
             button = false;
         if ( button == "INPUT") {
             button = target;
         } 
         if (button) {
            document.getElementById(button).addEventListener("click", function(event){
      		event.preventDefault()
      	});
            return false;
         }
      }
      

      und ein Auschnitt der Seite auf der alles eingebunden werden soll:

      <section id="content_wrapper">
      	<script>
      		list();
      		document.getElementById("content_wrapper").onclick = buttonClick;
      	</script>
      <section>
      

      Gruß
      Jo

      1. @@_j4nk3y

           var elementName = target.nodeName,
               button = false;
           if ( button == "INPUT") {
        

        Die Bedingung wird schwer zu erfüllen sein; false ist niemals gleich "INPUT".

        LLAP 🖖

        --
        “There’s no such thing as an ‘average’ user, but there is such a thing as an average developer.” —Vitaly Friedman in Accessibility Matters: Meet Our New Book, “Inclusive Design Patterns”
        Selfcode: sh:) fo:} ch:? rl:) br:> n4:& va:| de:> zu:} fl:{ ss:| ls:# js:|
      2. Hallo _j4nk3y

        $arr = array(a,b,c,d,e,f);
        $response = array();
        foreach($arr as $value)
        {
        	$markUp = '';
        	if($value == 'a' OR $value == 'c')
        	{
        		$markUp = $markUp.'<form action="b.php" method="post">
        					<input class="button" id="submit_'.$value.'" type="submit"/>
        				<form>';
        

        Wenn ich das richtig sehe brauchst du hier das <form> nicht. Das Absenden machst du nämlich rein mit Javascript. Es reicht ein <input type="button"> oder <button> ohne <form>.

        Ich würde hier den Wert in einem data-Attribut unterbringen, dann kannst du ihn später mit Javascript auslesen:

        <button data-value="DER WERT" class="button">...</button>
        

        Hab keine Ahnung von PHP aber da siehts vielleicht so aus

        '<button data-value="' . htmlspecialchars($value) . '" class="button">...</button>'

        function list()
        {
        	var hr = new XMLHttpRequest();
        	var url = "a.php";
        	
        	hr.onreadystatechange = function()
        	{
        		if (hr.readyState == 4 && hr.status == 200)
        		{
        			var div = document.getElementById("content_wrapper");
        			
        			var response = JSON.parse(hr.responseText);
        			
        			for(var key in response){
        					
        				div.innerHTML += response[key]['markUp'];
        

        Wenn du letztlich ein String baust und an innerHTML zuweist... wieso sendet der Server dann ein JSON und nicht einfach direkt HTML? Dann sparst du dieses mehrfache Zuweisen von HTML. dann würde reichen

        div.innerHTML = hr.responseText;
        
        funktion buttonClick (e) {
           
           var e = e || window.event;
           var target = e.target || e.srcElement;
        
           var elementName = target.nodeName,
               button = false;
           if ( button == "INPUT") {
               button = target;
           } 
        

        Ich glaub du willst hier testen, ob das target einer der Buttons ist. Das ist schon ganz richtig, aber der Test funktioniert so nicht.

        Ich würde auf die Klasse prüfen. Denn der Tag-Name ist nicht unbedingt eindeutig. Es gibt wahrscheinlich noch andere INPUTs die nicht diesen Ajaxrequest auslösen sollen.

        Wenn die Bedingung nicht zutrifft würd ich die Funktion einfach beenden:

        if (target.className !== 'button') {
          return;
        }
        

        if (button) { document.getElementById(button).addEventListener("click", function(event){ event.preventDefault() }); return false; }

        Das ist unnötig. Du bist hier bereits im Handler für den Clickevent und weißt dass einer der Buttons geklickt wurde. Du hast bereits das Eventobjekt als "e". Du kannst direkt das machen was du tun willst.

        Beim Event Delegation brauchen die einzelnen Buttons keine eigenen Clickhandler. Alles läuft durch den einen zentralen.

        event.preventDefault();
        // Wert aus dem data Attribut auslesen
        var value = target.getAttribute('data-value');
        // Mach was mit dem wert
        submit(value);
        
          document.getElementById("content_wrapper").onclick = buttonClick;
        

        Das ist okay!

        Viel Erfolg noch Sigg

        1. hi,

          Wenn ich das richtig sehe brauchst du hier das <form> nicht. Das Absenden machst du nämlich rein mit Javascript. Es reicht ein <input type="button"> oder <button> ohne <form>.

          Bist wohl neu hier? SELFHTML-Ideologie: Das Absenden von Formularen muss auch ohne JS funktionieren.

          Mich wunderts dass hier keiner nölt ;)

          1. Das Absenden von Formularen muss auch ohne JS funktionieren.

            Da hab ich nichts gegen. Der Code von j4nk3y schickt aber ohne JS keine Formdaten an den Server, oder?

            <form action="b.php" method="post">
            <input class="button" id="submit_'.$value.'" type="submit" onclick="submit('.$value.')"/>
            <form>
            

            Da wird nichts übertragen wenn ich das richtig sehe. Das müsste man schon umbauen damit b.php etwas bekommt.

            <form action="b.php" method="post">
            <input type="submit" name="theValue" value="' . htmlspecialchars($value) . '">
            </form>
            

            Sigg

          2. @@pl

            Wenn ich das richtig sehe brauchst du hier das <form> nicht. Das Absenden machst du nämlich rein mit Javascript. Es reicht ein <input type="button"> oder <button> ohne <form>.

            Bist wohl neu hier? SELFHTML-Ideologie: Das Absenden von Formularen muss auch ohne JS funktionieren.

            Vor allem – wie dedlfix anmerkte – auch per Enter-Taste.

            Mich wunderts dass hier keiner nölt ;)

            Ich kann ja nicht immer hier sein. ;-)

            LLAP 🖖

            --
            “There’s no such thing as an ‘average’ user, but there is such a thing as an average developer.” —Vitaly Friedman in Accessibility Matters: Meet Our New Book, “Inclusive Design Patterns”
            Selfcode: sh:) fo:} ch:? rl:) br:> n4:& va:| de:> zu:} fl:{ ss:| ls:# js:|
      3. Leider wird die Default- Aktion nicht unterdrückt und das Formular regulär abgeschickt. Sonst hätte ich ja kein Problem und müsste nicht Nachfragen.

        Deine Funktion muss false zurückgeben, dann wird das reguläre submit unterdrückt. Und wie gesagt, send() ohne Argument sendet keine Daten.

        MfG

        1. Hallo

          Deine Funktion muss false zurückgeben, dann wird das reguläre submit unterdrückt.

          Als Rückgabewert eines Eventhandlers false zu bestimmen um eine Standardaktion zu unterdrücken ist finsteres Mittelalter. Das sollte weder praktiziert noch empfohlen werden. Erstens ist es schlecht für die Lesbarkeit des Codes und zweitens entspricht es nicht dem standardisierten Ereignismodell des DOM. Darum schlägt diese Variante meines Wissens auch bei Eventhandlern fehl, die mittels addEventListener registriert wurden, was aber aus mehreren Gründen die grundsätzlich zu empfehlende Methode ist.

          function handler (event) {
            if (event.cancelable) {
              console.info('event is cancelable');
              event.preventDefault( );
              if (event.defaultPrevented) {
                console.info('default action prevented');
              }
            }
          }
          

          Darüber, ob eine Standardaktion unterdrückt werden kann, gibt der Wert der Eigenschaft cancelable des Eventobjektes auskunft, das Aussetzen der Aktion erfolgt mit der Methode preventDefault, und ob die Operation erfolgreich war, lässt sich am Wert der Eigenschaft defaultPrevented ablesen.

          Gruß,

          Orlok

      4. Tach!

        Leider wird die Default- Aktion nicht unterdrückt und das Formular regulär abgeschickt.

        Vorschlag für eine andere Vorgehensweise: Das Absenden des Ajax-Requests sollte keine Aktion des Buttons sein. Der sollte stattdessen ein normaler Submit-Button ohne irgendwelche Eventhandler sein. Du klinkst dich stattdessen in das submit-Event des Formulars ein und verhinderst dessen Default-Handler. Das hat den Vorteil, dass man nicht nur mit Klick auf den Button, sondern auch mit Entertastendruck das Formular absenden kannst und trotzdem deine Bearbeitung eintritt.

        dedlfix.

  2. hr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");

    ist Default, kannst weglassen

    hr.send();

    hr.send() sendet keine Daten.

    MfG