• Resolved gshell

    (@gshell)


    I am working on a plugin that allows members to register for an event. Most of it is working, however I think I have an issue with the sequence of how various portions are executed. I have an external PHP file that retrieves the registrants from a SQL table and displays them as a table. Each table row has a cancel button created with a unique ID to allow members to cancel their registration. When I include that file in an ajax callback function, it displays properly, but the cancel buttons do not do anything. If I ‘inspect element’ on the buttons the console log displays the correct ID for the cancel button, but it does not do anything. I suspect that the method I am using to display the registrant list with cancel buttons is affecting how they are processed (or not processed) by the JS file.

    Here is the basic sequence of the code that I have:

    At the top of the main plugin PHP file, I enqueue the JS file and define the Ajax URL.
    This is followed by the main plugin function which creates some HTML input fields and RSVP button. At the bottom of this function I define a div ID to be filled in by the ajax the callback function.

    	return '<div id="ajax_response"></div>';
    }; // end of main rsvp_event_rsvp function
    

    This is followed by 2 callback functions, one to retrieve the eventid from the page being viewed, and another to extract the user actions to RSVP for the event or cancel their reservation.
    I then create a short code for the main plugin function. The short code is appended to the bottom of the page being viewed.

    In the JS file, the first thing that happens, is the eventid is constructed from information on the page being viewed and an ajax call is created to the eventid_callback function below. The JS ajax call is as follows:

    		var event_category = ($jq( ".tribe-events-event-categories" ).text());
    		// alert ("Event Category is " + event_category);
    		var event_start = ($jq( ".tribe-events-start-date" ).attr("title"));
    		// alert ("Event Start Date is " + event_start);
    		var event_id = event_category.substring(0,1) + event_start.substring(0,4) + event_start.substring(5,7) + event_start.substring(8,10);
    		alert ("Event ID is " + event_id);
    
    		$jq.ajax({
    			url : ajax_rsvp.ajax_url,
    			type : 'post',
    			data : {
    				action: 'eventid_callback',
    				event_id_name : event_id
    			},
    			success:function(data) {
    				// This outputs the result of the ajax request
    				console.log(data);
    				// Return response to client side
    				$jq('#ajax_response').html( data ); 
    				return false;
    
    			},
    			error: function(errorThrown){
    				console.log(errorThrown);
    			}
    		}); // End of AJAX function
    

    Note the jquery command which fills in the div id=ajax_response defined in the main plugin function.
    The JS file then includes commands that process the various button clicks (RSVP or Cancel) and the ajax calls to the rsvp_callback function.

    add_action( 'wp_ajax_eventid_callback', 'eventid_callback' );
    function eventid_callback() {
    	$event_id = $_POST[event_id_name];
    	echo "<p>Event ID from eventid_callback is: {$event_id} </p>";
    	
    	include ('event-rsvp-list.php');
    	rsvp_event_list($event_id);
    		
    	die();
    }; // end of eventid_callback function
    

    This works properly, but the buttons defined in the event-rsvp-list.php file are not processed by the JS file commands.

    If I add the event list file into the main plugin php file, the registrant list is displayed properly and all buttons work so long as a I hardcode the eventid. The problem I have with this approach is that I don’t seem to be able to pass the eventid variable into the main plugin function that was processed by the eventid_callback function. I have tried making $event_id a global variable, and also trying to retrieve it from the ajax URL again with another $_POST command. Neither seemed to work. So far the only way I seem to be able to display the registrant list and have working cancel buttons is to hardcode the $event_id into the main plugin function. Anytime I display the list via the div element filled out from the ajax call, the buttons do not work.

    I realize this was lengthy and confusing, but just writing down and trying to explain the issue helped me. I would appreciate any thoughts that anyone might have.
    Thanks

Viewing 9 replies - 1 through 9 (of 9 total)
  • Moderator bcworkz

    (@bcworkz)

    Apologies if I missed something in your post, but I don’t see how you are capturing click events on buttons that are supposed to invoke some Ajax action. How this is done or not done is the crux of your problem I believe. Essentially, click events on new content added from the Ajax call you posted are not being captured. Every button needs an event listener associated with it.

    Perhaps the easiest way to do so is ensure all buttons of a particular type can all be selected by a JQuery selector. In other words, give all the buttons a common class. In the event handler, the clicked object is accessed with this. You can thus extract the related ID from that object and pass it along in your Ajax call. For example:

    $jq('.my-button').click(function() {
       var clickedOject = this;
       var clickedId = myExtractId( clickedObject );
       $jq.ajax({
          url : ajax_rsvp.ajax_url,
          type : 'post',
          data : {
             action: 'eventid_callback',
             event_id_name : clickedId
          },
          success:function(data) {
          // etc....

    Beware, this has very limited scope, it’s best to assign it to a variable with greater scope. For example, in the success: callback, $jq(this) is undefined but $jq(clckedObject) returns the clicked object.

    Thread Starter gshell

    (@gshell)

    bcworkz,

    Thanks for your input. I will have to study it more to see if it can help. I’ll try to explain better what I am doing.

    On page load, the eventid is parsed from information on the page and sent to the server via ajax.

    $jq(document).ready(function(){
    	if($jq("#tribe-events-content").hasClass("tribe-events-single")) {
    		// alert ("on single event page");
    		$jq(".tribe-events-nav-pagination").hide();
    		var event_category = ($jq( ".tribe-events-event-categories" ).text());
    		// alert ("Event Category is " + event_category);
    		var event_start = ($jq( ".tribe-events-start-date" ).attr("title"));
    		// alert ("Event Start Date is " + event_start);
    		var event_id = event_category.substring(0,1) + event_start.substring(0,4) + event_start.substring(5,7) + event_start.substring(8,10);
    		alert ("Event ID is " + event_id);
    
    		$jq.ajax({
    			url : ajax_rsvp.ajax_url,
    			type : 'post',
    			data : {
    				action: 'eventid_callback',
    				event_id_name : event_id
    			},
    			success:function(data) {
    				// This outputs the result of the ajax request
    				console.log(data);
    				// Return response to client side
    				$jq('#ajax_response').html( data );
    				return false;
    
    			},
    			error: function(errorThrown){
    				console.log(errorThrown);
    			}
    		}); // End of AJAX function
    

    Callback function:

    add_action( 'wp_ajax_eventid_callback', 'eventid_callback' );
    function eventid_callback() {
    	$event_id = $_POST[event_id_name];
    	echo "<p>Event ID from eventid_callback is: {$event_id} </p>";
    	
    	include ('event-rsvp-list.php');
    	rsvp_event_list($event_id); // cancel buttons do not work from this display
    		
    	die();
    }; // end of eventid_callback function
    

    Cancel Button creation in event-rsvp-list.php:

    <input type="button" class="cancel_btn" id="<?php echo $btn_id; ?>" name="<?php echo $btn_id; ?>" value="Cancel">
    

    Cancel Button processing in the JS file:

    	$jq(".cancel_btn").click(function(){
    		// Determine which cancel button was clicked
    		var btnid = event.srcElement.id;
    		var strlng = btnid.length;
    		var rowid = btnid.substring(11,strlng);
    		// alert("Cancel Row_ID " + rowid);
    		
    			$jq.ajax({
    				url : ajax_rsvp.ajax_url,
    				type : 'post',
    				data : {
    					action: 'rsvp_callback',
    					registrant_type : 'cancel',
    					delete_row : rowid
    				},
    				success:function(data) {
    					// This outputs the result of the ajax request
    					console.log(data);
    					// Return response to client side
    					//$jq('#rsvp_response').html( data );
    
    					alert("Sorry you will be unable to attend");
    					location.reload(true);
    
    				},
    				error: function(errorThrown){
    					console.log(errorThrown);
    				}
    			}); // End of AJAX function
    			
    	}); // End of Cancel Button function
    					
    }); // End of Main Document Ready Function
    

    In the main plugin function that is executed via a shortcode, I temporarily have the following after creating an input form. I realize that at this point, two copies of the registrant list are created. One through the eventid_callback function that displays correctly, but the cancel buttons do not work, and a second from the main plugin function that displays and works correctly, but I have to hardcode the $event_id variable.

    	$event_id = $_POST[event_id_name]; // not working
    	$event_id = "C20181201"; /* eventually build from event category and start date */
    	echo "<p>Event ID in MAIN following input table is: {$event_id} </p>";
    	include ('event-rsvp-list.php');
    	rsvp_event_list($event_id); // this works with hardcoded $event_id
    
    	return '<div id="ajax_response"></div>';
    	
    }; // end of rsvp_event_rsvp function
    

    If I could either pass the $event_id variable from the eventid_callback function to the main to replace the hardcoded variable, that should work, or figure out what I need to do to be able to make the JS file handle the Clicks on the cancel buttons added via the eventid_callback.

    Thanks for any advice you may have.

    Thread Starter gshell

    (@gshell)

    bcworkz,

    Clearly I still have a lot to learn. Since processing the hardcoded eventid in the main function appeared to work properly, I decided to have the eventid_callback function call that function directly with the variable $event_id passed to it instead of passing it to the rsvp_event_list function thinking that would replace the hardcoded value with a dynamic variable and all would be fine. Wrong! the result was the same. The registrant list displayed correctly but the JS file did not recognize or process the cancel buttons created by the registrant list function.

    This is why I have the suspicion that the problem is a timing issue. Think of the page as being 2 parts. The first part is the input form, the second part is the registrant list. If I hardcode the eventid into the main function both parts are created up front as the page is loaded and the JS file is able to recognize and process actions on all of the input fields and buttons. When the second part (the registrant list) is created after the page is loaded (as it must be in order for the eventid to be dynamically built from the page being displayed), the JS file does not recognize the cancel buttons created after the initial page load. Simply refreshing the page does not alleviate the issue.

    Earlier I was calling the registrant list via a shortcode, but as I moved toward creating the $event_id variable dynamically, I did not know how to add a variable into the shortcode definition. I was wondering if something like this could be made to work and whether calling the event list via a shortcode changes the timing sequence so that the JS file would recognize the buttons:

    add_shortcode( 'Event-List', 'rsvp_event_list($event_id)');
    
    Any advice would be appreciated.
    
    Moderator bcworkz

    (@bcworkz)

    You cannot pass PHP variables to functions through add_shortcode(). You only pass the name of the handler function, it’s not an actual function call. For procedural handlers, you just do
    add_shortcode( 'Event-List', 'rsvp_event_list');

    You can pass static values to the handler when entering the shortcode in the editor:
    post content with shortcode [Event-List id="A123123"] more content

    Or you can have your handler get values from elsewhere with normal PHP. BTW, I recommend shortcode names follow slug naming rules: all lowercase, alphanumeric, no special characters or punctuation except the hyphen, so ‘event-list’ would be a better name. Some WP objects cannot properly handle mixed case names, you get strange, difficult to debug errors. I don’t think this is the case with shortcode names. I can’t keep straight which matter and which do not, it’s best IMO to just apply the rule for all names.

    Your $jq(".cancel_btn").click() code is what I was looking for ?? You can work off the event object instead of using this, but you must collect it in your handler function:

    $jq(".cancel_btn").click( function( event ){
    		// Determine which cancel button was clicked
    		var btnid = event.srcElement.id;
                    // etc...

    The other thing is indeed a timing issue of a sort, but really more of a logic flaw. “Timing” implies a race condition, which is not the case here. When the .click() script runs on load, it assigns event listeners to all matching elements. When more content is added via Ajax, they will not have listeners attached because the code adding them had already run. From your Ajax success: handler, you would need to also add new event listeners to the added elements.

    You don’t want to call $jq(".cancel_btn").click() again because elements with existing listeners will have more added, resulting in the handler function being called multiple times. I suggest you instead use JavaScript .addEventListener() for each individual added element instead of using jQuery on all matching elements. If you really want to use jQuery, the selector must only target added elements and not existing elements.

    Thread Starter gshell

    (@gshell)

    I suggest you instead use JavaScript .addEventListener() for each individual added element

    I think that is the real issue. I have the same issue when attempting to add some CSS style to the elements. The elements added via the ajax call were not changed with the CSS coding.
    If I understand what you are saying correctly, I need to add the event listener in the ‘success’ section of my eventid ajax call which is what is displaying the registrant list and all the new cancel buttons. I’ll let you know how I make out.

    Thread Starter gshell

    (@gshell)

    OK, Not as easy as I had expected. Apparently the addEventListener method must be applied to specific elements by ID. I tried to apply it to a class=cancel_btn, but received an error that method was not allowed on those elements. Since I would have no idea how many cancel buttons are in the registrant list (cancel buttons only exist if the logged in user was also the same user that created the reservation in the first place), or what their IDs are, that would not be a solution path.
    Since the cancel buttons are only created via the registrant list function and the registrant list function is only called by the eventid_callback function, I decided to simply move the $jq(“.cancel_btn”).click(function() to the success area of the eventid ajax call. Viola! All is well!
    That pretty much completes the major programming efforts of this task. Now I need to move on to learning more about CSS so that I can make it ‘pretty’ and add a few minor tweaks.
    Thank you so much for all the help you have provided along the way. It has been a rewarding learning experience that I am sure still has more rewards to be earned.

    Moderator bcworkz

    (@bcworkz)

    You’re welcome, I’m glad it’s working for you.

    I almost hate to point this out, but using $jq(“.cancel_btn”).click(function() in the success: code would be adding listeners to elements that already have a listener. The result is clicking one of the older cancel buttons will execute the cancel Ajax request multiple times. There’s no outward evidence that this is a problem, the code is probably just trying to delete a row that does not exist after the first time around. Other than logging warnings and being inefficient, there’s really no harm done.

    You could apply the “if it ain’t broke, don’t fix it” maxim and not worry about it. The proper solution is indeed not easy. You would need to alter the output from adding elements so that a unique ID or other unique attribute is included so that only one element is targeted, whether by jQuery or JavaScript. One solution would be to assign the ID you are extracting as an ID attribute of the input cancel button. Not only will this allow targeting of individual elements, it will make extracting the ID value simpler.

    If you can’t be bothered to implement something like this, I understand completely. My only wish is that you understand what’s going on. Whether you actually do something about it doesn’t matter to me ??

    Thread Starter gshell

    (@gshell)

    bcworkz,
    Maybe I don’t understand what you are saying, but I don’t believe using the $jq click function is adding listeners to elements that already have listeners as I moved the function from where it was in the code to the success code. I did not duplicate it. Since the cancel buttons are only created by the event_list function that is called by the eventid_callback, I just added their event listener in the success code for that callback function. If I understand it correctly, the ajax exchange creates the cancel buttons, then the success code adds the event listener to the elements that it just created. I don’t see where they could have received another event listener prior to that.

    Moderator bcworkz

    (@bcworkz)

    OK, I don’t have a good grasp of what your DOM is doing, so you are likely correct. Sorry for my confusion. Either way, it works, that’s what’s important.

Viewing 9 replies - 1 through 9 (of 9 total)
  • The topic ‘Firing Order Question’ is closed to new replies.