I'm trying to rewrite some script. This script takes some data from data attributes and rebuild block with them. All was alright, but I need to do it via AJAX. Here my modified script:
(function($){
jQuery.fn.someItem = function()
{
var make = function() {
var _$this = $(this);
var $thumb = _$this.find('.thumb');
function init()
{
$thumb.on('click', function()
{
if (!$(this).hasClass('active')) setNewActiveItem($(this));
return false;
});
}
function setNewActiveItem($newItem)
{
$.ajax({
url: '/ajax-item?id=' + $newItem.data('id'),
type: 'GET',
success: function(response)
{
_$this.replaceWith(response);
**init();**
}
});
}
init();
};
return this.each(make);
};
})(jQuery);
All working fine, but after Ajax call and block replaced, I can't apply ajax-call in modified block again. I guess that I need to reinit "init()" function after "replaceWith()", but how to do it? Thank you for help.
You need to use a delegated event handler in the init() when attaching your click event to the .thumb elements. Try this:
var make = function() {
var _$this = $(this);
function init() {
_$this.on('click', '.thumb', function() {
if (!$(this).hasClass('active'))
setNewActiveItem($(this));
return false;
});
}
function setNewActiveItem($newItem) {
$.ajax({
url: '/ajax-item?id=' + $newItem.data('id'),
type: 'GET',
success: function(response) {
_$this.replaceWith(response);
}
});
}
init();
};
This works by assigning the click handler to the parent element and inspecting the click event as it bubbles up the DOM. It means that you can append any .thumb element at any time and not have to re-assign any new click handlers as the one defined on the parent will catch all.
Related
I have two pages with posts and reviews. The functions for the reply buttons are similar and both using unbind function in document ready event. Somehow, just one function will work even if they are accessing different classes. They both work when one is commented out. I appreciate any help and ideas. Thank you!
//Replies Posts
$(document).ready(function () {
$(document).unbind().on("click", ".btnReplySubmit", function() {
if (!$.trim($(this).closest(".myRepliesForm").find(".textareaReply").val())) {
alert("Empty Content");
}
else {
$.ajax({
url: "/Replies/Create/",
type: "post",
cache: false,
data: $(this).closest(".myRepliesForm").serialize(),
success: function() {
$(".reloadComments").load(location.href + " .reloadComments");
$(".reloadComments").show("slow");
}
});
$(this).closest(".myRepliesForm").find(".textareaReply").val("");
}
return false;
});
});
//Reply Review
$(document).ready(function () {
$(document).unbind().on("click", ".btnReplySubmitReview", function () {
if (!$.trim($(this).closest(".myRepliesFormReview").find(".textareaReplyReview").val())) {
alert("Empty Content");
}
else {
$.ajax({
url: "/ReviewReplies/Create/",
type: "post",
cache: false,
data: $(this).closest(".myRepliesFormReview").serialize(),
success: function () {
$(".reloadCommentsReview").load(location.href + " .reloadCommentsReview");
$(".reloadCommentsReview").show("slow");
}
});
$(this).closest(".myRepliesFormReview").find(".textareaReplyReview").val("");
}
return false;
});
});
Well if you use unbind on both event handlers, one of the functions is going to be inevitably unbound. You also do not need two ready calls as one is enough and finally, you can chain your event handler binding as follows :
$(document).ready(function () {
$(document).unbind()
.on("click", ".btnReplySubmit", function() {
if (!$.trim($(this).closest(".myRepliesForm").find(".textareaReply").val())) {
// ...
}
else {
// ...
}
return false;
})
.on("click", ".btnReplySubmitReview", function () {
if (!$.trim($(this).closest(".myRepliesFormReview").find(".textareaReplyReview").val())) {
// ...
}
else {
// ...
}
return false;
});
});
Because unbind will clear all previous bindings.
But.. why complicating things with bind/unbind on document, you could just do a click listener on both buttons :
$(document).ready(function () {
$('.btnReplySubmit').click(function(){
// alert(1);
});
$('.btnReplySubmitReview').click(function(){
// alert(2);
})
});
the main issue I see here is that you are unbinding events attached to $(document). also, according to jquery's api, you shouldn't be using unbind anymore as it is already deprecated.
instead, you should structure your code like so
$(document).ready(function() {
$('. btnReplySubmit').click(function() {
// your code here
})
$('. btnReplySubmitReview').click(function() {
// your code here
})
})
this makes your event declarations much clearer and much easier to read
All the answers are correct. I may just add that the selector in .on(events [, selector] [,data], handler) method is the event trigger, but event is bound to document in your case.
I want to call a function every time my update was successful. The update is working my only concern is the alert pop-up every successful update.
$.post(
{
url: 'update_question.php',
data:
{
id: id,
new_question: newText,
},
success: function()
{
that.replaceWith("<section>"+newText+"</section>");
if(text != newText)
{
popup();
}
}
});
var popup = function () {
$(document).ready (function(){
$("#myWish").click(function showAlert() {
$("#success-alert").alert();
$("#success-alert").fadeTo(2000, 500).slideUp(500, function(){
$("#success-alert").alert('close');
});
});
});
};
var popup = function () {
$("#success-alert").alert();
$("#success-alert").fadeTo(2000, 500).slideUp(500, function(){
$("#success-alert").alert('close');
});
};
On the first update, pop-up showed but it doesn't show on the 2nd update
I think it'll solve your issue
$.post(
{
url: 'update_question.php',
data:
{
id: id,
new_question: newText,
},
success: function()
{
that.replaceWith("<section>"+newText+"</section>");
if(text != newText){
popup();
}
}
});
function popup() {
$("#success-alert").alert();
$("#success-alert").fadeTo(2000, 500).slideUp(500, function(){
$("#success-alert").alert('close');
});
};
The $(document).ready jquery function waits until the DOM is loaded into your browser before it executes the javascript code contained within it's function scope {}.
So remove $(document).ready from your code.
Also note that single page applications only need to list $(document).ready once and all the listener events you setup are defined within it's body.
So you should have it listed somewhere at least once and then you define all your initial event listeners within its body.
On click I run a function that will do an ajax submission for each form that has the .red_active class. After the ajax submission or after the complete function I want to remove the parent's .red_active class. This is what I tried, can you help me spot my mistake?
$('.edit_old').click(function(){
$('.slider_edit').each(function(){
if($(this).hasClass('red_active')){
$(this).find('.edit_form_slide').each(function(){
$(this).on('submit', function(e) {
e.preventDefault();
var data = $(this).serialize();
var url = $(this).attr('action');
$.ajax({
type: "POST",
url: url,
data: data,
success: function (data) {
console.log('submitted '+ url);
//$(this).parent().removeClass('.red_active');
},
error: function () {
console.log('fail');
}
});
});
$(this).submit();
//$(this).submit().parent().removeClass('.red_active');
});
}
});
});
The issue is because within the success handler the this keyword does not reference the .edit_form_slide as it does in the each() handler. You need to store the reference of this in a variable:
$('.edit_old').click(function () {
$('.slider_edit').each(function () {
var $sliderEdit = $(this);
if ($sliderEdit.hasClass('red_active')) {
$sliderEdit.find('.edit_form_slide').each(function () {
var $editFormSlide = $(this); // store 'this' in a variable
$editFormSlide.on('submit', function (e) {
e.preventDefault();
var data = $editFormSlide.serialize();
var url = $editFormSlide.attr('action');
$.ajax({
type: "POST",
url: url,
data: data,
success: function (data) {
console.log('submitted ' + url);
$editFormSlide.parent().removeClass('.red_active'); // to use here, within the other scope
},
error: function () {
console.log('fail');
}
});
});
$editFormSlide.submit();
});
}
});
});
Note that I did the same for the .slider_edit selector too, just to keep things consistent. If you have nested this references it can get confusing to keep track of what is referencing what, without a named variable.
First you can optimize and remove the if and .find lines
$('.slider_edit. red_active . edit_form_slide').each(function(){
has the same effect than :
$('.slider_edit').each(function(){
if($(this).hasClass('red_active')){
$(this).find('.edit_form_slide').each(function(){
And next to find a parents with a class, the best way is to use .parents() and all beware of the this in your function, the this in the success function is not the this you are looking to. You should save the $(this) before the ajax call in a var and reuse it into success callback.
Full correction :
$('.edit_old').click(function() {
$('.slider_edit.red_active .edit_form_slide').each(function() {
var $formSlide = $(this);
$formSlide.on('submit', function(e) {
e.preventDefault();
var data = $(this).serialize();
var url = $(this).attr('action');
$.ajax({
type: "POST",
url: url,
data: data,
success: function(data) {
$formSlide.parents('.red_active:first').removeClass('.red_active');
},
error: function() {
console.log('fail');
}
});
}).submit();
});
});
I'm having a problem with the click events not working using a Javascript MVC Controller.
TEST.Assignments.AssignmentsController = function (element) {
var elements = {
activeAssignmentsPanel: $('#lpn-activeAssignments_Cont'),
assignmentViewLink: $("#lpn-activeAssignments_Cont table tr th a")
};
var _this = this;
var model = new TEST.Assignments.AssignmentModel();
this.buildAssignmentsList = function () {
var assignments = model.getActiveAssignmentsList({
assignmentMode: "active",
mock: true,
success: function (data) {
dust.render("ActiveAssignmentsPanel", data, function(err, out) {
elements.activeAssignmentsPanel.append(out);
});
}
});
};
this.getAssignmentDetails = function(assignmentId) {
console.log(assignmentId);
};
//bind all events
elements.assignmentViewLink.click(function (e) {
console.log("blah");
console.log($(this).data("assignmentKey"));
});
};//end assignments controller
$(function () {
var assignmentsController = new TEST.Assignments.AssignmentsController();
assignmentsController.buildAssignmentsList();
});
If you look at the //bind events, I have a click function there that should be working. But it is not. The constructor is being called and the elements are traced out correctly. Any idea why the click event won't work?
I assume the assignmentViewLink elements are created and appended in the success callback. If so, it looks like a sequence problem. When you bind the click event, the assignmentViewLink elements have not been created yet, and hence, the click eventhandler isn't attached.
//bind all events
// assignmentViewLink is empty []
elements.assignmentViewLink.click(function (e) {
console.log("blah");
console.log($(this).data("assignmentKey"));
});
To verify this, move the elements.assignmentViewLink(...) into the success callback.
I have some jquery that looks like this,
$('.career_select .selectitems').click(function(){
var selectedCareer = $(this).attr('title');
$.ajax({
type: 'POST',
url: '/roadmap/step_two',
data: 'career_choice='+selectedCareer+"&ajax=true&submit_career=Next",
success: function(html){
$('.hfeed').append(html);
$('#grade_choice').SelectCustomizer();
}
});
});
My problem is that if the user keeps clicking then the .hfeed keeps getting data appended to it. How can I limit it so that it can only be clicked once?
Use the one function:
Attach a handler to an event for the elements. The handler is executed at most once per element
If you wanted the element to only be clicked once and then be re-enabled once the request finishes, you could:
A) Keep a state variable that updates if a request is currently in progress and exits at the top of the event if it is.
B) Use one, put your code inside a function, and rebind upon completion of request.
The second option would look like this:
function myClickEvent() {
var selectedCareer = $(this).attr('title');
var that = this;
$.ajax({
type: 'POST',
url: '/roadmap/step_two',
data: 'career_choice='+selectedCareer+"&ajax=true&submit_career=Next",
success: function(html){
$('.hfeed').append(html);
$('#grade_choice').SelectCustomizer();
},
complete: function() {
$(that).one('click', myClickEvent);
}
});
}
$('.career_select .selectitems').one('click', myClickEvent);
You can either use a global variable like
var added = false;
$('.career_select .selectitems').click(function(){
if(!added) {
// previous code here
added = true;
}
});
or use .one("click", function () { ... }) instead of the previous click function to execute the handler at most once per element. See http://api.jquery.com/one/ for more details.