Adding event handler to non-existent class? - javascript

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 */
}
});

Related

Remove dynamically created button's history - jQuery

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 :-)

jQuery removes first div only once

I have a function:
function removeDiv() {
var topmost = jQuery('.xx');
var totContent = topmost.find('.zz').length;
var $target = jQuery('.xx').find('.zz').eq(0);
if(totContent > 5) {
$target.hide('slow', function(){ $target.remove(); });
}
}
I use it in my ajax call, to remove extra div then there are more than 5, hovewer it remove first div only once!
And this is how ajax call looks:
function saveClubs(array) {
for(i=0; i<array.length; i++) {
var id = array[i];
jQuery.ajax({
type: "GET",
async: true,
url: 'index.php?option=com_events&task=club.save&id=' + id,
dataType: 'json',
success: function(data) {
jQuery('.xx').append('<div class="zz">'+data+'</div>');
removeDiv();
}
});
}
}
Any ideas ?
This is Paul Roub's answer, posted as an answer rather than a comment:
The likely problem is that since you're doing a bunch of ajax calls in a loop, they tend to complete at the same time, and so you end up repeated fading out the same element (since it's still there until it's done fading).
The minimal changes fix would be to, say, add a class as you're fading it out:
function removeDiv() {
// Get the container (I take it there's only one .xx element)
var topmost = jQuery('.xx');
// Get the child elements that aren't fading
var zz = topmost.find('.zz').not('.fading');
// Too many?
if(zz.length > 5) {
// Yup, add 'fading' to the first one and fade it out
// Note that there's no need for the $target variable
zz.eq(0).addClass('fading').hide('slow', function(){ $(this).remove(); });
}
}
The problem is this:
var $target = jQuery('.xx').find('.zz').eq(0);
It's always 0 index.
function removeDiv(x) {
var topmost = jQuery('.xx');
var totContent = topmost.find('.zz').length;
var $target = jQuery('.xx').find('.zz').eq(x);
if(totContent > 5) {
$target.hide('slow', function(){ $target.remove(); });
}
}
function saveClubs(array) {
for(i=0; i<array.length; i++) {
var id = array[i];
jQuery.ajax({
type: "GET",
async: true,
url: 'index.php?option=com_events&task=club.save&id=' + id,
dataType: 'json',
success: function(data) {
jQuery('.xx').append('<div class="zz">'+data+'</div>');
removeDiv(i);
}
});
}
}
LIVE EXAMPLE HERE
NOTE
IN the Fiddle above, try to change this var $target = jQuery('.xx').find('.zz').eq(x); harcoding the value of x to 0 and it'll happen just once.

Event handler doesn't work anymore after html rerendering

I have the following structure in my js file:
$.getJSON("data/file.json")
.done(function(data) {
var loadHTMLfunction = /* some code */
loadHTMLfunction();
// updates display based on user filter selection
$("#select-section").on("change", function() {
$("article").find(".myClass").remove();
loadHTMLfunction();
});
// text swap event
$(".summary-link").on("click", function() {
var el = $(this),
tmp = el.text();
el.text(el.data("text-swap"));
el.data("text-swap", tmp);
});
})
.fail(function(jqxhr, textStatus, error) {
// error handling
});
my issue is that my text swap event works well when the page is loaded for the first time but it doesn't work anymore once the user used the selection event (this event is a select html element to update display).
Is there something wrong in my js file structure or something wrong in my code (or in both!)?
Thanks!
I feel the coupling on your functions are too tight. Once $.getJSON.done, you should really just assign the data to a variable and bind your event handlers outside of your scope of the ajax call. A refactored version of the code might look something like this:
(function() {
var storedData;
$.getJSON("data/file.json", function(data) {
storedData = data;
});
.error(function(jqXHR, textStatus, errorThrown) {
alert("error: " + textStatus + ", Description: " + jqXHR.responsetext);
});
var loadHTMLfunction = function() {
// your code
};
loadHTMLfunction(); // You ought to tell us what this function is
// else we don't know if it's redundant or not
// to run so repetitively.
$("#select-section").on("change", function() {
$("article").find(".myClass").remove();
loadHTMLfunction(); // I feel like this is an issue.
});
$(".summary-link").on("click", function() {
var el = $(this),
tmp = el.text();
el.text(el.storedData("text-swap"));
el.storedData("text-swap", tmp);
});
}());
You should include
// text swap event
$(".summary-link").on("click", function() {
var el = $(this),
tmp = el.text();
el.text(el.data("text-swap"));
el.data("text-swap", tmp);
});
in the function loadHTMLfunction.
loadHTMLfunction = function(){
//your code
// text swap event
$(".summary-link").on("click", function() {
var el = $(this),
tmp = el.text();
el.text(el.data("text-swap"));
el.data("text-swap", tmp);
});
}
You probably are doing some action that removes the element ".summary-link" and you just bind event first time.

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>

jQuery on or live?

I recently deployed an infinite scroll to an app that I have build and found that sometimes I need to click twice for something to happen.
My app has likes, and once the dom had loaded, i need to click on the like button twice before it changes, then once i click on the other ones it's okay but I always have to click once for the app to almost "wake up"
Is there a better solution?
$(document).ready(function() {
function runUpdate(url, item) {
$.ajax({
type: "GET",
url: url,
cache: false,
success: function(data){
if (data == '200') {
removeAddColor(item);
}
}
});
}
$('.mini-like').live('click', function(){
$('.mini-like').toggle(
function() {
var item = $(this);
var href = item.attr('href');
runUpdate(href, item);
},
function() {
var item = $(this);
var rel = item.attr('rel');
runUpdate(rel, item);
}
);
});
function removeAddColorFollow(item) {
var href = $(this).attr('href');
var rel = $(this).attr('rel');
if (item.hasClass('btn-success')) {
$(item).removeClass('btn-success').attr('href', href).attr('rel', rel);
$(item).find('i').removeClass('icon-white');
} else {
$(item).addClass('btn-success').attr('href', rel).attr('rel', href);
$(item).find('i').addClass('icon-white');
};
}
});
Well unless I'm completely wrong, you only attach the toggle event to .mini-like after it has been clicked once. Try to just replace
$('.mini-like').live('click', function() {...
With
$(function() {...
To attach the toggle event handler on document ready instead of on click
The code $('.mini-like').live('click',... should be placed inside $(document).ready()
You can use .on in place of .live. As .on is a new method and .live is deprecated now you should use .on
UPDATE
The re-written version will be
$(document).ready(function(){
$('.mini-like').on('click', function(){
$('.mini-like').toggle(
function() {
var item = $(this);
var href = item.attr('href');
runUpdate(href, item);
},
function() {
var item = $(this);
var rel = item.attr('rel');
runUpdate(rel, item);
}
);
});
});
function runUpdate(url, item) {
$.ajax({
type: "GET",
url: url,
cache: false,
success: function(data){
if (data == '200') {
removeAddColor(item);
}
}
});
}
function removeAddColorFollow(item) {
var href = $(this).attr('href');
var rel = $(this).attr('rel');
if (item.hasClass('btn-success')) {
$(item).removeClass('btn-success').attr('href', href).attr('rel', rel);
$(item).find('i').removeClass('icon-white');
} else {
$(item).addClass('btn-success').attr('href', rel).attr('rel', href);
$(item).find('i').addClass('icon-white');
};
}

Categories

Resources