I would really appreciate your help, as I been looking over and over and completely lost in what could go wrong.
I have an Ajax call to web service, and from this call I am building some HTML structure. Up to this point everything is good, the data get pulled and I can visually see the structure.
However as my code continues and I try to read those elements later for some more manipulations, JavaScript doesn't see them at all, and on element counts I am getting zero.
Some relevant code from the project.
This function that runs first and basically builds the needed HTML piece
$.ajax({
type: "POST",
url: "HistoryEventsWebService.asmx/getEventsData",
data: event_types,
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (result) { //on success
var results = result.d;
$('#insidediv_container').empty();
for (var i = 0; i < results.length; i++) {
var insidediv = '';
insidediv += '<div class="insidediv">';
insidediv += '<span class="month">' + results[i].month + '</span><span class="day">' + results[i].day + '</span><span class="year">' + results[i].year + '</span>';
insidediv += '<div class="header"><span>Jan 08, 2010 - event one</span> </div>';
insidediv += '<div class="contentdiv">' + results[i].event_desc + '</div>';
insidediv += '</div>';
$('#insidediv_container').append(insidediv);
}
}
});
return false;
Right after this upper function is done, my code is continued to next function, and that's where I have my problem!
var currentbox = 0;
var current_box_old = 0;
var box_size = ($('.insidediv').width() + 1).toString();
var box_count = $('.container2 > div > div').length - 1;
//console.log(box_count);
var min_range = 0;
setScale(); //making slider visual elements
//slider code
$("#slider").slider({
min: min_range,
max: box_count,
value: 0,
animate: true,
//step: 1,
slide: function (event, ui) {
current_box_old = currentbox;
currentbox = ui.value;
if (currentbox > current_box_old)
$('#insidediv_container').animate({ "margin-left": "-=" + box_size + "px" }, "normal");
else
$('#insidediv_container').animate({ "margin-left": "+=" + box_size + "px" }, "normal");
}
});
Thank you in advance.
The reason this isn't working is that the whole of the code you want executed must be inside the ajax success method. Because the ajax post is asynchronous, the ajax method 'returns' almost instantly and flow immediately flows to your second block of code. However, because it got there so quickly, the ajax post hasn't received it's results yet, so the success method hasn't been called yet, which in turn means that the elements you're looking for have not yet been inserted into the DOM.
If these are the rough modular steps in what your code is doing:
$.ajax (A) -> http post (B) -> success (C) -> add elements to DOM (D)
|
v
update new DOM elements (E)
Then the actual flow of code is A, B, E, C, D and not A, B, C, D, E.
Hope this helps!
Apparently JQuery Ajax has function complete. I ended up wrapping my second piece of code into function and making call from complete.
Thanks everyone for the help.
Related
I have a situation where I am updating a the DOM with information from a 3rd party website at the same time that I am creating an object on the backend with ajax. Sometimes the ajax call returns slower than the separate job responds through ActionCable.
On the front-end I'm rendering the 3rd party results but when the DOM hasn't been updated I end up with an un-updatable DOM. Is there another way to handle a delay other than just with a setTimeout? Because if for some reason I don't wait long enough, the DOM update will still fail.
EDIT: Code of what I'm trying to do.
connectPosts: function connectPosts(){
var self = this;
if (!webSockets) { return }
App.cable.subscriptions.create({
channel_id: channel_id,
channel: 'PostsChannel'
},{
received: function(data){
var $element = $('#' + data['object_type'] + '_' + data['object_id'] + ' .container');
if($element.length == 0)
setTimeout(function(data){self.buildElement(data)}, 1000, data);
else
self.buildElement(data);
}
});
},
buildElement: function buildElement(data){
var $element = $('#' + data['object_type'] + '_' + data['object_id'] + ' .container');
var content = HandlebarsTemplates['posts/element'](data);
$element.html(content);
if($element.find('.author-' + self.currentUser.id)){
$element.find('a').show();
}
}
};
I am using Jquery Datatable in my code. I am loading the data via an ajax request. For each row created in my table there is one button called delete. The on-click event of this button reads an attribute called "r_id" and then applies a soft-deletion in my database. My code is like the following:
$(document).ready(function () {
var parity = 3;
var tr = "";
$.ajax({
url: "https://" + window.myLocalVar + "/api/rewards/findAllRewardsByStatusID/" + parity,
type: "GET",
dataType: 'json',
contentType: 'application/json',
success: function (rewards) {
if (rewards.length > 0) {
for (var i = 0; i < rewards.length; i++) {
var reward = rewards[i].Reward;
var reward_price = "$ " + rewards[i].Price;
var r_id = rewards[i].RewardID;
tr += '<tr r_id="' + r_id + '">\
<td>' + reward + '</td>\
<td>' + reward_price + '</td>\
<td>\
<button r_id="' + r_id + '" type="button" class="btn btn-danger delete">delete</button>\
</td>\
</tr>';
if (i % 10 == 0 || i == rewards.length - 1) {
$("#submitted_rewards > tbody").append(tr);
tr = "";
}
}
}
$('#submitted_rewards').on('init.dt', function () {
// click event for each tr
$("#submitted_rewards tbody tr td:not(:last-child)").on("click", function () {
var r_id = $(this).parent().attr("r_id");
window.location.href = "./reward-info.aspx?id=" + r_id;
});
$(".delete").click(function () {
var r = $(this).attr("r_id");
$("tr[r_id=" + r + "]").fadeOut();
});
}).DataTable({
"scrollX": true,
stateSave: true
});
},
error: function (err) {
if (err) {
console.log(err);
}
}
});
});
This code loads the data in my Jquery Datatable called "submitted_rewards" correctly. The page size of the table is set to '10' as a default (10 rows per page). Now, I tried to go to the second page and click on the row to redirect to another page OR if I tried to click on the delete button to delete a row and it did not work! Those events work only for the page that a user lands on the first time. I am saving the state of the table that mean if I switched from page 1 to page 2 then refreshed that page. The events of each row in page 2 ONLY will work.
I tried to use the event initComplete but it did not work. Any idea how to solve this problem.
Thanks!
If you attach handlers to DOM elements and those DOM elements are destroyed your handlers will be of no effect anymore and the related code will be garbage collected at some point.
When you change pages by doing window.location.href = ..., you are effectively replacing your DOM elements (and script) with a new set from the page that is replacing it. Consequently, your handlers will no longer apply.
You can address this in a couple ways.
1) You will need to add script to each page you are loading and have it execute when the DOM is ready, applying handlers to the DOM elements on that page.
2) Do another ajax request to get new data and replace certain elements on your page. Use jQuery's .delegate() to attach handlers automatically to certain elements even as you add them later.
The reason why it is not working because I am appending every 10 rows to the table.. I appended all the rows of the table at one shot and then have the clicking events then it is going to work.
If I want to keep loading every 10 rows. then I have to move the events inside the if statement like the following:
if (i % 10 == 0 || i == rewards.length - 1) {
$("#submitted_rewards > tbody").append(tr);
tr = "";
$("#submitted_rewards tbody tr td:not(:last-child)").on("click", function () {
var r_id = $(this).parent().attr("r_id");
window.location.href = "./reward-info.aspx?id=" + r_id;
});
$(".delete").click(function () {
var r = $(this).attr("r_id");
$("tr[r_id=" + r + "]").fadeOut();
});
}
I'm working on a twitter clone in JS + jQuery for a pre-course to a development program, so if this is an obvious fix - let me know!
I have a problem I'm unable to solve: "click on username, page returns with that user's last tweets".
The only thing I could come up with is, an event handler on the <a> tag filter the page. However I'm vastly inexperienced and unclear in how to proceed.
Any ideas?
note- I removed some code for brevity.
$(document).ready(function() {
var $body = $('body');
$body.html();
var stream = function() {
var index = streams.home.length - 1;
while (index >= 0) {
var tweet = streams.home[index];
var $tweet = $('<div class="tweetbody"></div>');
$tweet.text(': ' + tweet.message);
$tweet.appendTo($body);
index -= 1;
var link = $('<a>', {
text: tweet.user,
href: '#',
}).prop('outerHTML');
$tweet.html('#' + link + ': ' + tweet.message);
}
};
Here's the <a> tag event:
//click on a username to see that user's timeline.
$('a').on('click', function() {
console.log("a tag is clicked ");
console.log(this);
});
}();
}); //end document ready body
In the on click function you could perhaps do either of the two things, so go to another page, like redirecting to a new page
//click on a username to see that user's timeline.
$('a').on('click', function() {
//console.log("a tag is clicked ");
//console.log(this);
window.location = '/some_file.php?get_tweets='+tweet.user;
});
or, use an ajax call to do the same:
//click on a username to see that user's timeline.
$('a').on('click', function() {
//console.log("a tag is clicked ");
//console.log(this);
$.ajax({
type: "GET",
url: "/some_file.php",
data: {
'user' : tweet.user
},
success: function(msg) {
$body.html(msg);
});
});
In both cases some_file.php should format the content.
I'm having a problem trying to make this code work. The purpose is simply to enable a textbox when the option "Bus" is selected from a DropList.
At the moment I have a for loop running through and disabling all the necessary boxes (there are 15 identical rows). At the same time it is enabling a different function which is based on changing the selection in the same box, which works. Whereas the function in question doesn't work.
Here is the function:
$(function () {
for(var i=0;i<15;i++){ //loop through each of the rows
$("#Select" + i + "Box_C").change(callbackFactory(i)); //This is a working function
$("#Select" + i + "Box_C").change(toBus(i)); //This one isn't working
$("#Text" + i + "Box_Q1").prop('disabled', true); //this one is working
};
function busEnabler(num){
$("#Text" + num + "Box_Q1").prop('disabled', false);
};
function toBus(numm){
var jk = numm //Closures confuse me
if ($("#Select" + numm + "Box").val() === "Bus"){
$("#Text" + numm + "Box_Q1").prop('disabled', false);
console.log(jk);
busEnabler(jk);
}
else {
$("#Text" + numm + "Box_Q1").prop('disabled', true);
console.log($("#Select" + numm + "Box_C") + "=" + $("#Select" + numm + "Box_C").val());
}
};
});
The ID's are made up (the real ones are horribly named - not my choosing) so if there is a typo in the ID's it's irrelevant.
Also as a side note I can't seem to log anything to the console after the page has loaded (using FireBug).
callbackFactory:
function callbackFactory(i){
console.log('changing');
return function(){
transportChange($("#_Q0_Q"+ i +"_Q3_C").val(), i);
};
The problem is in the way you are attaching the onchange events.
The following statement (that you have used) does not attach any method.
$("#Select" + i + "Box_C").change(toBus(i));
So whenever the value of the select box changes, the 'toBus' method is never called.
I have created a Fiddle. Please refer that.
JSFiddle
Use the following code for your purpose and please show the function callbackFactory also, so that I can resolve your complete problem.
$(doucment).on('change', 'select[id^=Select]',function() {
$('input[id^=Text]').prop({disabled:true});
var id = $(this).attr('id');
var txtId = id.replace('Select','Text').replace('Box','Box_Q1');
if($(this).val() == 'Bus') {
$('#'+txtId).prop({disabled:false});
}
});
I have built a dropdown menu system, everything works when tested independently, the problem I have is in the code below. I use the jQuery ready function to build the menu bar from an external array (menubar[]). Here I am trying to get the mouseover event to call the dropdown() function, but using a different argument for each anchor tag.
So rolling over the first should call dropdown(0), the second dropdown(1) and so on.
$(document).ready(function () {
for (i in menubar) {
var declaration = '<a href="' + baseurl + '/' + menubar[i].url +
'" class="menutitle">' + menubar[i].name + '</a>';
var a = $(declaration).mouseover(function () {
dropdown(i);
}).mouseout(function () {
activeTimer = setTimeout("removedropdowns()", 100);
});
$("#menu").append(a);
}
});
The code is calling dropdown(6); on each rollover. How can I pass the loop variable (i) into the mouseover function as a literal/static value!
I got this working fine in FF by using
.attr('onMouseOver','javascript:dropdown('+i+');')
but that wasn't firing for some versions of IE, so I switched to the jQuery mouseover, which fires, but I have the issue above :(
Your actual problem is that each of your mouseover callbacks uses the same i you increase i all the way up to 6, the callbacks still point to the same i and therefore all use 6 as the value.
You need to make a copy of the value of i, you can do this by using an anonymous function.
$(document).ready(function () {
// you should use (for(var i = 0, l = menubar.length; i < l; i++) here in case menubar is an array
for (var i in menubar) {
var declaration = '<a href="' + baseurl + '/' + menubar[i].url +
'" class="menutitle">' + menubar[i].name + '</a>';
(function(e) { // e is a new local variable for each callback
var a = $(declaration).mouseover(function () {
dropdown(e);
}).mouseout(function () {
activeTimer = setTimeout(removedropdowns, 100); // don't use strings for setTimeout, since that calls eval
});
$("#menu").append(a);
})(i); // pass in the value of i
}
});
$(function() {
$(menubar).each(function(i){
$("#menu").append('' + menubar[i].name + '');
});
$("#menu a").hover(
function(){
dropdown($(this).index());
},
function(){
activeTimer = setTimeout("removedropdowns()", 100);
}
);
});
First, don't use for..in but rather ordinary loop.
Second, I would just append the links first then apply the events later:
$(document).ready(function() {
for (var i = 0; i < menubar.length; i++) {
$("#menu").append('' + menubar[i].name + '');
}
$("#menu a").each(function(index) {
$(this).mouseover(function() { dropdown(index); }).mouseout(function() { activeTimer = setTimeout("removedropdowns()", 100); });
});
});
Have a look here and here.
To capture the current value of i, you need to pass it as a parameter to another function where it can be captured as a local variable:
Try using jQuery's each() function:
jQuery(function() {
jQuery.each(menubar, function(index, element) {
var declaration = '' + element.name + '';
var a = $(declaration).mouseover(function() { dropdown(index); }).mouseout(function() { activeTimer = setTimeout("removedropdowns()", 100); });
$("#menu").append(a);
});
});
In JavaScript, if you don't declare your variable, it is defined globally. To fix this, add "var" in front of your i looping variable like this. UPDATE: As Sime noticed (see comment), you also need to pass the variable into the function, otherwise you form a closure on the i.
$(document).ready(function() {
for(var i in menubar) {
var declaration = '' + menubar[i].name + '';
var a = $(declaration).mouseover(function(i) { dropdown(i); }).mouseout(function() { activeTimer = setTimeout("removedropdowns()", 100); });
$("#menu").append(a);
}
});