Jquery Dynamic Selector Problem - Disabling Buttons When Enableclientvalidation=True

Hi Guys

I have a button in a separate view called btnSubmitMisc.

Attached to this button is a function that changes it’s class to make it appear ‘disabled’ after it was clicked:




<?php

	echo CHtml::link($caption,'#', array(

		'id'=>'btnSubmitMisc',

		'submit'=>array($actionUrl),

		'class'=>'myCHtmlButton',

	));

?>


<?php

	Yii::app()->clientScript->registerScript('submitTest', "

	$('#btnSubmitMisc').click(function(){

		if($('#btnSubmitMisc').hasClass('myCHtmlButton'))

			{

				$('#btnSubmitMisc').removeClass('myCHtmlButton');

				$('#btnSubmitMisc').addClass('myCHtmlButtonClicked');

			}

			else

			{

				return false;  // Don't submit if class is myCHtmlButtonClicked

			}

		});

	");

?>



Now whenever I need a button in my main-view, I just use this:




<?php

$this->renderPartial('...btnSubmitMisc', array(

	'actionUrl'=>'contact',

	'caption'=>'Submit',

));

?>



The problem is that if I add more of these buttons to the main-view, the jquery does not work correctly because all buttons then have the same ID. I can pass each button a unique id from the main-view, but how do you get the jquery to dynamically use the ID of the correct button? How do you pass the ID to jquery? I don’t think that I can use classes, because I’m already using classes to make the button able/disable.

Dear Friend

I just tried to simulate your scenario in my localhost.

I placed the script in main view.

The whole idea is to use the jQuery’s live method.

one.php.(main.php)




//This is linkbox. Every subsequent links are placed here.


<div id="linkBox">


<?php echo CHtml::link('click',array('test/two'));?>


</div>


//The script to attach the click event only to every new element created.This also sends an ajax request to get the partial view.


<?php

Yii::app()->clientScript->registerSCript('test','

$("#linkBox  a:last").live("click",function(){

	

	$.ajax({

		url:"index.php?r=test/two",

		type:"POST",

		success:function(data)

                        {

			   $("#linkBox").append("<div>"+data+"</div>");

			}		

		});

	

	$(this).attr("href","#"); //DISABLING THE LINK.

	return false;

	});

');

?>



The controller

TestController.php




<?php

class TestController extends Controller

{

	public function actionOne() {

		

		$this->render('one');

		}

		

	public function actionTwo() {

		

		$this->renderPartial('two',array(),false,true);

		}

}




partial view

two.php




<?php

echo CHtml::link('click',array('test/two'));

?>



This is simple scenario. This can be done by Jquery itself without getting the partial view by AJAX.

.live() is deprecated and should not be used anymore.

@Gerhard Liebenberg: These scenarios are best solved using event delegation… google it… there are many tutorials on how to do it.

Its the way to go and I see a lot of questions regarding how to handle click events for example on dynamically loaded forms.

Furthermore, event delegation is the right way, not only for dynamically created elements, but when there are many elements( lets say buttons ) that you want to bind a click event.

Instead of binding single event to 100 buttons, its good to bind one time one event to a parent container, which then handles the events for all children.

The other thing you want to know is that the context of the callback you bind is set to the DOM element, which caused it.

In your callback you can use $(this) to get to the button that was clicked.

From there you can get its ID like this:

var clickedId = $(this).attr( ‘id’ );

There is no need for you to manually pass any kind ID, as you asked.

And some notes… not directly concerning your problem, but more as a general tips:

Cache jquery selectors, when using them more than once.

Selecting by ID, as in your case ( $( ‘#id’ ) ), is fast, because its using the internal getEleemntById. But other selectors can have significant performance impact on your application.

So instead of doing $(’#btnSubmitMisc’) every time, you should do:

var mySelector = $(’#btnSubmitMisc’);

mySelector.click() …

mySelector.click() …

mySelector.click() …

Hi,

Left only this. I was too haste with my reply. Sorry.

  1. Pass additional data to renderPartial method. See docs for details.

To keep things short and to the point:




//in your view

$this->renderPartial('myFancyButton', array(

    'myFancyAndUniqueId' => 'something_unique',

));

//in your partial view file

<?php

        echo CHtml::link($caption,'#', array(

                'id'=>$myFancyAndUniqueId,

                'submit'=>array($actionUrl),

                'class'=>'myCHtmlButton',

        ));

?>


<?php

        Yii::app()->clientScript->registerScript('submitTest', "

        $('#$myFancyAndUniqueId').click(function(){

                if($('#$myFancyAndUniqueId').hasClass('myCHtmlButton'))

                        {

                                $('#$myFancyAndUniqueId').removeClass('myCHtmlButton');

                                $('#$myFancyAndUniqueId').addClass('myCHtmlButtonClicked');

                        }

                        else

                        {

                                return false;  // Don't submit if class is myCHtmlButtonClicked

                        }

                });

        ");

?>



This should do.

Cheerio.

Dear genn

Thanks for sharing valuable information.

delegate is far better than live when performance is considered.

Then my code should be changed like this.




<?php

Yii::app()->clientScript->registerSCript('test','

$("div").delegate("#linkBox  a:last","click",function(){  

        

        $.ajax({

                url:"index.php?r=test/two",

                type:"POST",

                success:function(data)

                        {

                           $("#linkBox").append("<div>"+data+"</div>");

                        }               

                });

        

        $(this).attr("href","#"); //DISABLING THE LINK.

        return false;

        });

');

?>



Thanx for all the replies Guys.

I will try event delegation first.

Regards

OK I added this event delegation code:




$('.divContainingLinkButtons').on("click", "a:.myCHtmlButton", function(){

	$(this).removeClass('myCHtmlButton');

	$(this).addClass('myCHtmlButtonClicked');

});


$('.divContainingLinkButtons').on("click", "a:.myCHtmlButtonClicked", function(event){

	event.preventDefault();

	return false;

});



It works fine if you don’t have ‘enableClientValidation’=>true in the view because it:

  • intercepts the submit AFTER the button was already disabled and then

  • fires a second submit after validation - resulting in the above code preventing the submission in total.

So I thought of adding an event delegation function on the form to fire ONLY when the form is finally submitted AFTER validation.




$("form").on("submit", function(event) { 

   // disable buttons here 

});



But the problem is that this function fires whenever a submit button is clicked - even if the form’s submission is stopped by client side validation errors. And then the button is disabled.

So I don’t have a solution yet that works with client side validation. Tomorrow I will try seenivasan’s code.

I am bumping this topic, because the focus changed

from: Jquery Dynamic Selector Problem

to: Disabling Buttons When Enableclientvalidation=true

I have tried various methods to disable all Link-buttons on my form after submit. All methods work if you don’t have Enableclientvalidation=true.

The reason seems to be that clientvalidation: intercepts the submit; does validation; and sends a second submit if validation succeeds. The problem is that with the second submit, the button is already disabled - so the form is then not submitted at all.

Any other ideas?

I do not use ActiveForm and therefore cannot help you with that.

My suggestion would be to take a look at how the form is being submitted and look for some hooks.

I guess it was designed in a manner that it could be easily extended with some callbacks, events or whatever.

I just checked again what you want to achieve in your first post… If its only about disabling the submit button(s), I don’t think its a good approach to do it this way.

I would go and extend the CHTML class, which creates all html elements. You can add your own function there to create a button with a special class. Then you have to modify the client side code and add the necessary functionality, using event delegation. This way you can automatically register any necessary JS code and creating new button would come to one line:

echo MyHTML::myCustomLink( /* arguments…ID, class, name… etc. **/ )

The question here is how customizable is the JS code, responsible for the active forms… maybe someone, who has experience with it can help you further.

Thanx for the advice Genn.

Yes, maybe there is some attribute set after CActiveform performed clientside and ajax validation without errors. Then you can test for this attribute and only disable the buttons if attribute = true.