Remove dynamically created button's history - jQuery - javascript

this is my first entry on StackOverFlow.
I'm working on a project and it needs jQuery to perform a master/detail table layout.
I have to work in asp.net C#, master and detail table generate dynamically.
So what is my problem:
I generate the master table with ajax:
function refreshMasterTable() {
xhr = $.ajax({
type: "GET",
url: "tablefunctions.aspx?mode=showmastertable",
success: function (html) {
$("#tbl_master").html(html);
prevAjaxReturned = true;
$('input[type=button]').click(function () {
var bid, trid;
bid = (this.id);
trid = $(this).closest('tr').attr('id');
if ($("#detail_" + trid).length == 0) {
detailShow = true;
pointer = $(this).closest('tr');
pointer.after("<tr><td colspan=5><div id=detail_" + trid + "></div></td></tr>");
$.get("tablefunctions.aspx?mode=showdetailtable&id=" + trid, function (response) {
$('#detail_' + trid).html(response);
});
$(document).on('click', '#submitMasterData', function () {
value = $('#name').val();
$.get("tablefunctions.aspx?mode=mastertableupdate&id=" + trid + "&name=" + value);
refreshMasterTable();
});
} else {
detailShow = false;
$(this).closest('tr').next("tr").remove();
}
});
}
});
};
In tablefunctions.aspx there is an entry, what generates the submit button:
html.Append("<tr><td colspan=\"2\" align=\"right\"><input type=\"submit\" id=\"submitMasterData\" /></td></tr>");
So the problem begins here. Each time when I ask a new detail row in the master table, a new submitMasterData instance of button creates and the $(document).on('click', '#submitMasterData', function () event triggers on every previous values. If I reload the page, the first detail request is OK, but the "collection" begins again.
$("#submitMasterData").remove(); didn't solve the problem. Sorry for my bad English, if something is not clear, please ask me...

The problem is the $(document).on() function is binding a new event each time a button is clicked without removing any of the previous events. You can use the off() function to remove the old ones in queue.
function refreshMasterTable() {
xhr = $.ajax({
type: "GET",
url: "tablefunctions.aspx?mode=showmastertable",
success: function (html) {
$("#tbl_master").html(html);
prevAjaxReturned = true;
$('input[type=button]').click(function () {
var bid, trid;
bid = (this.id);
trid = $(this).closest('tr').attr('id');
if ($("#detail_" + trid).length == 0) {
detailShow = true;
pointer = $(this).closest('tr');
pointer.after("<tr><td colspan=5><div id=detail_" + trid + "></div></td></tr>");
$.get("tablefunctions.aspx?mode=showdetailtable&id=" + trid, function (response) {
$('#detail_' + trid).html(response);
});
//need to unbind all the previously attached events
$(document).off('click', '#submitMasterData');
$(document).on('click', '#submitMasterData', function () {
value = $('#name').val();
$.get("tablefunctions.aspx?mode=mastertableupdate&id=" + trid + "&name=" + value);
refreshMasterTable();
});
} else {
detailShow = false;
$(this).closest('tr').next("tr").remove();
}
});
}
});
};
You can view a proof of concept in this JS fiddle: https://jsfiddle.net/bfc6wzt8/
Hope that helps :-)

Related

AJAX sending multiple requests after button click

I just want one click to equal one submit in my jQuery code.
I've read quite a few posts on this same topic but I think mine is different. I do have a mouseleave and focusout event that I'm using to find errors in user input. Those functions feed down into the function that is submitting multiple times. The more times I hit mouseleave and focusout the more times my Ajax request is submitted. But I need mouseleave and focusout to continue to work and check the users input, that's why I'm not using one. Please see my code below, the function that I think is submitting multiple times is handleButtonClicksAfterError
function getCreditAmountToSend(modal){
console.log("getCreditAmountToSend");
var checkBox = $(modal).contents().find("#fresh-credit-checkbox");
checkBox.change(function(){
if($(checkBox).is(":checked")) {
var creditAmount = +(sessionStorage.getItem("creditAmount"));
sessionStorage.setItem('amountToSend', creditAmount);
}
});
var pendingCreditAmount = $(modal).contents().find("#pending_credit_amount");
pendingCreditAmount.on({
mouseleave: function(){
if(pendingCreditAmount.val() != ""){
adminForGetPendingCredit(modal);
}
},
focusout: function(){
if(pendingCreditAmount.val() != ""){
adminForGetPendingCredit(modal);
}
}
});
}
function adminForGetPendingCredit(modal){
console.log("adminForGetPendingCredit");
var checkBox = $(modal).contents().find("#fresh-credit-checkbox");
if(!$(checkBox).is(":checked")) {
var enteredAmount = +($(modal).contents().find("#pending_credit_amount").val());
var creditAmount = +(sessionStorage.getItem("creditAmount"));
sessionStorage.setItem('enteredAmount', enteredAmount);
doWeDisplayError(modal,creditAmount, enteredAmount);
}
}
function doWeDisplayError(modal,creditAmount, enteredAmount){
console.log("doWeDisplayError");
$(modal).contents().find("#fresh-credit-continue-shopping").prop("disabled", false);
$(modal).contents().find("#fresh-credit-checkout").prop("disabled", false);
if(creditAmount < enteredAmount){
$(modal).contents().find("#pending_credit_amount").val("");
$(modal).contents().find("#fresh-credit-continue-shopping").prop("disabled", true);
$(modal).contents().find("#fresh-credit-checkout").prop("disabled", true);
displayError();
}
else{
handleButtonClicksAfterError(modal, enteredAmount);
}
}
function handleButtonClicksAfterError(modal, enteredAmount){
// this is the problem!!!!!!!!!!!!!
console.log("handleButtonClicksAfterError");
sessionStorage.setItem('amountToSend', enteredAmount);
var continueButton = $(modal).contents().find("#fresh-credit-continue-shopping");
continueButton.click(function() {
modal.hide();
});
var checkoutButton = $(modal).contents().find("#fresh-credit-checkout");
checkoutButton.click(function() {
console.log("handleButtonClicksAfterError");
sendData();
});
}
function displayError(){
console.log("displayError");
$(function(){
$("#fresh-credit-iframe").contents().find("#pending_credit_amount").attr("placeholder", "Whoops, that was too much");
$("#fresh-credit-iframe").contents().find("#pending_credit_amount").attr({
class: "form-control form-control-red"
});
sessionStorage.removeItem('enteredAmount');
});
}
This is the function that actually POSTs the data
function sendData(){
var amountToSend = sessionStorage.getItem("amountToSend");
var products = $.parseJSON(sessionStorage.getItem("products"));
console.log("sendData");
console.log("This is the amount to send " + amountToSend);
$.ajax({
url: "/apps/proxy/return_draft_order",
data: {amountToSend, products},
type: "POST",
dataType: "json",
complete: function(data) {
window.location.href = data.responseText;
console.log("This is the URL from poll " + data.responseText );
return false;
},
});
}
It ended up being super simple.. I just needed the jQuery off method.. I attached it to the button before click and everything is peachy.. Looks like this:
checkoutButton.off().click(function(){});
off clears all the previous event handlers and then just proceeds with Click
Pretty cool, to read more check it out here
use async:false to prevent multiple request
$(document).off('click').on('click', function(){
$.ajax({
type:'POST',
url: ,
async:false,
cache: false,
data:{}
,
success: function(data){
},
error:function(data){
}
});
});

Click function doesn't work after ajax call in dynamic element (Backbone)

I've create dynamic popup in my Backbone.view by clicking button:
var Section = Backbone.View.extend({
className: 'sqs-frontend-overlay-editor-widget-section',
events:{
'click .sqs--section--control__edit': 'Section_control'
},
initialize: function(){
},
render: function(){
this.$el.append(_.template(_section).apply(this.options));
return this.$el;
},
Section_control: function(){
var me = this;
require(['View/Popup/Section_control'], function(_Section_control){
var sec = new _Section_control({popup: popup, sec: me.options.section});
var popup = new Popup({content: sec.render()});
});
}
});
return Section;
in the created dynamic popup i have button with trigger:
events:{
'click .module-invert-mode': 'invert'
},
invert: function(e){
console.log('hello');
if(this.options.sec.hasClass('.module-invert')) {
console.log('yse');
}
this.options.sec.toggleClass('module-invert');
this.options.sec.trigger('invertChange');
},
and button invertChange trigger:
el.on("invertChange", function(e){
var section = el.parents('section');
var index = section.index();
var model = collection.at(index);
model.set(Helper.sectionToObj(section),{doReload: true})
});
take a look at the {doReload: true} function that i call in invertChange:
change: function(model, options){
me = this;
if( model._changing && options.doReload ) {
$.ajax({
url: 'wp-admin/admin-ajax.php',
type: 'post',
data: {
action: 'getShortcode',
shortcode: model.attributes.shortcode
},
success: function (data) {
//var section = $(data);
me.$el.find('section:eq(' + model.collection.indexOf(model) + ')').replaceWith(data);
me.add( model, model.collection );
//me.collection.add({shortcode: model.attributes.shortcode}, {at: section.index()});
}
});
}
},
the problem is when I create dynamic popup and click on the button with invertChange trigger, ajax works only once, when I click on button in popup again, ajax doesn't works ( next ajax request works only if close and create dynamic popup again). How I can call ajax without constantly closing and opening my dynamic popup?
The problem that you have code which overrides child views
me.$el.find('section:eq(' + model.collection.indexOf(model) + ')').replaceWith(data);
And this listener is not able to handle event
el.on("invertChange", function(e){
because your code
this.options.sec.trigger('invertChange');
doesn't trigger event on correct view, it has lost the reference to this view after replaceWith()
As a solution you need parse your data object and apply each changes locally to elements
something like this
$(data).find("* [attr]").each(function(i, el) {
var $el = $(el),
attr = $el.attr("attr"),
$parent = me.$el.find('section:eq(' + model.collection.indexOf(model) + ')');
if ($el.is("div, span")) {
$parent.find('[attr=' + attr + ']').html($el.html());
} else if ($el.is("img")) {
$parent.find('[attr=' + attr + ']').attr("src", $el.attr("src"));
} else if ($el.is("a")) {
$parent.find('[attr=' + attr + ']').attr("href", $el.attr("href"));
} else if (attr == "image_back_one") {
$parent.find('[attr=' + attr + ']').attr("style", $el.attr("style"));
} else {
console.log($el);
}
});

Adding event handler to non-existent class?

I've seen questions that relate to non-existent elements, but not non-existent classes. Here's what I want to do. When a button of class "see_answer" is clicked, I want to remove the class and replace it with "see_question". However, my click function for a button, once its class is "see_question", is not running. I have tried $(document).on("click", ".see_question", function(event ) and I have tried $(".see_question").on("click", function(event) {etc.... Thanks for the help! My code is below:
$(document).ready(function() {
// initialize variables
var lang = "javascript";
var qno = 1;
var prevText; // holds question/answer
var language = lang + ".html";
// set up tabs, and keep track of which one is clicked
$("#myTabs").tabs({
activate: function (event, ui) {
var active = $("#myTabs").tabs("option", "active");
lang = $("#myTabs ul > li a").eq(active).attr("href");
lang = lang.replace("#", "");
}
});
/* REMINDERS
actual qa part: blah_language
*/
// set up question
$.ajax({
url: language,
dataType: "html",
success: function(data) {
$("#blah_"+lang)
.text($(data).find("#1").text());
},
error: function(r) {
alert("whoops, error in initialization");
}
});
$(".next_question").on("click", function(event) {
event.preventDefault();
var id = $(this).attr("id").replace("next_question_", "");
var language = id + ".html";
var doc = "#blah_" + id;
$.ajax({
url: language,
dataType: "html",
success: function(data) {
var num = "#" + qno;
$(doc)
.text($(data).find(num).text());
qno = qno + 1;
},
error: function(r) {
alert("whoops");
}
});
prevText = "";
});
// SHOW ANSWER
$(".see_answer").on("click", function(event) {
event.preventDefault();
var id = $(this).attr("id").replace("see_answer_", "");
var prev = "#blah_" + id;
var answers = id + "_answers.html";
// Save the question
prevText = $(prev).text();
var obj = $(this);
$.ajax({
url: answers,
dataType: "html",
success: function(data) {
var num = "#" + 3;
$(prev)
.text($(data).find(num).text());
},
error: function(r) {
alert("whoops");
}
});
obj.val("See Question");
obj.removeClass("see_answer");
obj.addClass("see_question");
event.stopPropagation();
});
$(document).on("click",".see_question", function(event) {
event.preventDefault();
obj = $(this);
event.preventDefault();
var id = $(this).attr("id").replace("see_answer_", "");
var prev = "#blah_" + id;
$(prev).text(prevText);
obj.val("See Answer");
obj.removeClass("see_question");
obj.addClass("see_answer");
});
})
Click handling for .see_question elements is delegated to document. For .see_answer elements, a click handler is attached directly. Therefore, swapping the class names will have an undesirable effect.
when see_answer is in force, a click will trigger the "see_answer" handler.
when see_question is in force, a click will trigger the "see_question" handler AND the "see_answer" handler, which is still attached.
There's a number of ways to do this properly. From where you currently are, the simplest solution is to delegate click handling of .see_question and .see_answer elements to document.
$(document).on("click", ".see_answer", function(event) {
...
});
$(document).on("click", ".see_question", function(event) {
...
});
Combine the 2 handlers and figure out which version it is by hasClass() before you change the classes around
$(document).on("click", ".see_question, .see-answer", function(event ){
var $btn =$(this), isAnswer = $btn.hasClass('see_answer');
// we know which one it is so can switch classes now
$btn.toggleClass('see_answer see_question');
if(isAnswer){
/* run code for answer version */
}else{
/* run code for question version */
}
});

2 javascripts are conflicting

I have 2 javascripts that are conflicting with eachother, the newer one (Zeroclipboard) conflicts with the older one (delete row) and won't let the delete row one work. The moment i removed the zeroclipboard one, delete worked.
Tried adding jQuery.noConflict(); but didn't seem to work. By reading few solutions, I decided to remove $ signs, but still no.
I have a files.php file, including the header.php file. I am adding the custom.js file in header.php, which holds many functions for operations across the project, including the delete row function. Whereas, the newer script for ZerClipboard is in files.php itself.
Older one, to delete a table row on delete icon click, which won't work after I add the next:
custom.js
function deleteRow()
{
var current = window.event.srcElement;
while ( (current = current.parentElement) && current.tagName !="TR");
current.parentElement.removeChild(current);
}
$(document).ready(function()
{
$('table#delTable td a.delete').click(function()
{
if (confirm("Are you sure you want to delete?"))
{
var fid = $(this).parent().parent().attr('fid');
var str=$(this).attr('rel');
var data = 'fid=' + $(this).attr('rel') + '&uid=' + $(this).parent().attr('rel');
var deletethis = '#tr' + $(this).attr('rel');
var parent = $(this).parent().parent();
$.ajax(
{
type: "POST",
url: "delete.php",
data: data,
cache: false,
success: function(msg)
{
$(deletethis).fadeOut('slow', function() {$(this).remove();});
}
});
}
});
$('table#delTable tr:odd').css('background',' #FFFFFF');
});
ZeroClipboard's JS and SWF, along with this js to copy some text on clipboard on Share icon click:
files.php
<script type="text/javascript" src="js/ZeroClipboard.js"></script>
<script language="JavaScript">
var clip = null;
function $(id) { return document.getElementById(id); }
function init()
{
clip = new ZeroClipboard.Client();
clip.setHandCursor( true );
}
function move_swf(ee)
{
copything = document.getElementById(ee.id+"_text").value;
clip.setText(copything);
if (clip.div)
{
clip.receiveEvent('mouseout', null);
clip.reposition(ee.id); }
else{ clip.glue(ee.id); }
clip.receiveEvent('mouseover', null);
}
</script>
I used this blog post for implementing multiple zerclipboard - http://blog.aajit.com/easy-multiple-copy-to-clipboard-by-zeroclipboard/
And, here's the HTML source generated by the files.php page - http://jpst.it/tlGU
Remove the follow function definition of your second script:
function $(id) { return document.getElementById(id); }
Because this is redefining your $ object in window context, due when you use $ in your first script you're not using jquery, instead you're using your new function definition.
Hope this helps,
Here is how you should use noConflict() :
function deleteRow()
{
var current = window.event.srcElement;
while ( (current = current.parentElement) && current.tagName !="TR");
current.parentElement.removeChild(current);
}
jQuery.noConflict(); // Reinitiating $ to its previous state
jQuery(document).ready(function($) // "Protected" jQuery code : $ is referencing jQuery inside this function, but not necessarily outside
{
$('table#delTable td a.delete').click(function()
{
if (confirm("Are you sure you want to delete?"))
{
var fid = $(this).parent().parent().attr('fid');
var str=$(this).attr('rel');
var data = 'fid=' + $(this).attr('rel') + '&uid=' + $(this).parent().attr('rel');
var deletethis = '#tr' + $(this).attr('rel');
var parent = $(this).parent().parent();
$.ajax(
{
type: "POST",
url: "delete.php",
data: data,
cache: false,
success: function(msg)
{
$(deletethis).fadeOut('slow', function() {$(this).remove();});
}
});
}
});
$('table#delTable tr:odd').css('background',' #FFFFFF');
});
And in files.php:
<script src="js/ZeroClipboard.js"></script>
<script>
var clip = null;
function $(id) {
return document.getElementById(id);
}
function init() {
clip = new ZeroClipboard.Client();
clip.setHandCursor(true);
}
function move_swf(ee) {
copything = document.getElementById(ee.id + "_text").value;
clip.setText(copything);
if (clip.div) {
clip.receiveEvent('mouseout', null);
clip.reposition(ee.id);
} else {
clip.glue(ee.id);
}
clip.receiveEvent('mouseover', null);
}
</script>

Javascript function only works after page refresh

I have a forum that has a "subscribe" and "unsubscribe" button. When you hit it once, it changes but then doesn't switch back again until the page is refreshed.
http://jsfiddle.net/7QPyL/
Here's my code:
$(document).ready(function () {
// Subscribe to topic subscribedtotopic
$(".notsubscribedtotopic").click(function () {
var topicid = $(this).attr('rel');
$(this).slideUp('fast');
$(this).html('Unsubscribe From Topic');
$(this).removeClass('notsubscribedtotopic').addClass('subscribedtotopic');
$(this).slideDown();
$.get("/base/Solution/SubScribeToTopic/" + topicid + ".aspx",
function (data) {
var result = $('value', data).text();
});
return false;
});
// UnSubscribe to topic subscribedtotopic
$(".subscribedtotopic").click(function () {
var topicid = $(this).attr('rel');
$(this).slideUp('fast');
$(this).html('Subscribe To Topic');
$(this).removeClass('subscribedtotopic').addClass('notsubscribedtotopic');
$(this).slideDown();
$.get("/base/Solution/UnSubScribeToTopic/" + topicid + ".aspx",
function (data) {
var result = $('value', data).text();
});
return false;
});
});
It does not work because the class is not there when you add the event handler, so it can not find the element.
$(".notsubscribedtotopic") and $(".subscribedtotopic") do not magically select new elements as they are added to the page.
You should write one function and just toggle the state.
$(".subscribe").click(function () {
var link = $(this);
var isSubscribbed = link.hasClass("subscribedtotopic");
link.toggleClass("subscribedtotopic notsubscribedtotopic");
...
});
I would set up your code to be something like this:
$(document).ready(function () {
// Subscribe to topic subscribedtotopic
function subscribetotopic() {
var topicid = $(this).attr('rel');
$(this).slideUp('fast');
$(this).html('Unsubscribe From Topic');
$(this).removeClass('notsubscribedtotopic').addClass('subscribedtotopic');
$(this).slideDown();
$.get("/base/Solution/SubScribeToTopic/" + topicid + ".aspx",
function (data) {
var result = $('value', data).text();
});
return false;
}
// UnSubscribe to topic subscribedtotopic
function unsubscribetotopic() {
var topicid = $(this).attr('rel');
$(this).slideUp('fast');
$(this).html('Subscribe To Topic');
$(this).removeClass('subscribedtotopic').addClass('notsubscribedtotopic');
$(this).slideDown();
$.get("/base/Solution/UnSubScribeToTopic/" + topicid + ".aspx",
function (data) {
var result = $('value', data).text();
});
return false;
}
});
And then make a simple onclick call in your HTML element, like so:
<button onclick="subscribetotopic()">Subscribe</button>
If you'd prefer to keep your code the way it is, then clarify which element you want to have active.
Example (simply set a div w/ id mymainelement and button w/ id subscribebutton):
$(#mymainelement .subscribebutton).click(function() { ... });
If you'd prefer to toggle the button instead of having two separate elements, check out #epsacarello's answer.

Categories

Resources