I have div element as
<div class="preview-image hide"><img src="{{STATIC_URL}}ui-anim_basic_16x16.gif"></div>
The hide class belongs to Twitter Bootstrap 2.3.2, the preview-image basically adds some styling to the element and used as handle for JavaScript.
I have jQuery code as below where
$loading.show() and $loading.hide() are not working.
The surprising this is when I run $preview.parent().find('.preview-image').show() from console, its working!!
(function($) {
$(document).ready(function() {
var SET_TIME = 6000;
$('[data-preview]').click(function() {
var $this = $(this);
var $preview = $('#' + $this.data('presponse'));
var $loading = $preview.parent().find('.preview-image');
$loading.show();
$.ajax({
});
$loading.hide();
});
});
})(window.jQuery);
Because $.ajax() is an asynchronous call, the $loading.hide() is being called (as it appears to the user) immediately after the $loading.show(). In order to circumvent this, you should make the $loading.hide() call after your AJAX call is complete. One way to do this is:
var $loading = $preview.parent().find('.preview-image');
$loading.show();
$.ajax({
}).always(function() {
$loading.hide();
});
Is the issue that it's hiding straight away? I think the hide needs to be within a success function of the AJAX call rather than being after it.
Eg.
$.ajax({
success: function() {
$loading.hide();
}
});
Related
I am getting an id that is not addressable by jquery ("#"+id).something .
At document start I have a :
var g_justClicked = '';
$.ajaxSetup({
beforeSend:function(event){
if(g_justClicked) {
console.log('g_justClicked='+g_justClicked+' tagName='+$('#'+g_justClicked).tagName);
};
var wOffset = $('#'+g_justClicked).offset();
$('#loading').show();
},
complete:function(){
$('#loading').hide();
}
});
At document end I have another script (all elements with class spinner should set the global variable 'g_justClicked'):
$(document).ready(function () {
$('.spinner').click(function() {
g_justClicked = $(this).attr('id');
console.log('.spinner.click: g_justClicked='+g_justClicked);
});
This works fine, the variable is set and displayed correctly in ajaxSetup.
BUT: referencing it in tagName= or in wOffset = with
$('#'+g_justClicked).
results in
"TypeError: wOffset/tagName is undefined"
Note: all ids start with several characters, t.e. "boxshow12345" is a typical id.
What am I doing wrong?
I think was able to reproduce your scenario here: https://jsfiddle.net/mrlew/qvvnjjxn/3/
The undefined in your console.log is because you're accessing an inexistent jQuery property: .tagName. This property is only available to native HTML Element.
To retrieve the tag name from a jQuery Object, you should use: .prop("tagName"), or access the property accessing the native element with $('#'+g_justClicked)[0].tagName
So, if you change
console.log('g_justClicked='+g_justClicked+' tagName='+$('#'+g_justClicked).tagName);
to:
console.log('g_justClicked='+g_justClicked+' tagName='+$('#'+g_justClicked).prop("tagName"));
Will successfully log: g_justClicked=boxshow12345 tagName=BUTTON, as expected.
Note: In order to your logic work, you have to click .spinner first.
Your problem is that your ajax setup runs regardless of whatever you do in the click handler, and it runs before you even setup that handler. The initial value for g_justClicked is empty string, and this is what it tries to access in $('#'+g_justClicked), hence the error.
If you want to click the spinner and then pass the id to the beforeSend, do it like this:
$(document).ready(function() {
$('.spinner').click(function() {
var g_justClicked = this.id; //simplify this a bit
console.log('.spinner.click: g_justClicked=' + g_justClicked);
// call ajax
_setupAjax( g_justClicked );
});
});
function _setupAjax(g_justClicked) {
$.ajaxSetup({
beforeSend: function(event) {
if (g_justClicked) {
console.log('g_justClicked=' + g_justClicked + ' tagName=' + $('#' + g_justClicked).tagName);
};
var wOffset = $('#' + g_justClicked).offset();
$('#loading').show();
},
complete: function() {
$('#loading').hide();
}
});
}
UPDATE
If you don't want a separate function, just move your ajax setup into the click handler:
$(document).ready(function() {
$('.spinner').click(function() {
var g_justClicked = this.id; //simplify this a bit
console.log('.spinner.click: g_justClicked=' + g_justClicked);
// call ajax setup
$.ajaxSetup({
beforeSend: function(event) {
if (g_justClicked) {
console.log('g_justClicked=' + g_justClicked + ' tagName=' + $('#' + g_justClicked).tagName);
};
var wOffset = $('#' + g_justClicked).offset();
$('#loading').show();
},
complete: function() {
$('#loading').hide();
}
});
});
});
OK #mrlew.
Answer: I tried your .prop appoach, but still got "undefined". Now back to the roots:
The goal is to get the id of any element that was clicked to modify the busy indicators position, while ajax is running. Newly I am back to my original approach, without global variable and parameter passing:
$(document).ready(function () {
$('.spinner').click(function() {
_setupAjax();
});
});
which works, and:
function _setupAjax() {
$.ajaxSetup({
beforeSend: function() {
$('#loading').show();
wJustClicked = $(this).attr('id'); /// <- that doesnt work!
console.log("_setupAjax wJustClicked="+wJustClicked);
console.log('_setupAjax tagName=' + $('#' + wJustClicked).prop("tagName"));
....defining css based on id (no problem)..
which yields "undefined" twice. I tried so many ways to get that f.... id.
#mrlew
thanks a lot for your help. Meanwhile I found the solution. All trouble came from a timing problem. Here is what works (for all DIV, SPAN and IMG of class=spinner and having an id:
$(document).ready(function () {
_setupAjax();
$('.spinner').click(function() {
wJustClicked = $(this).attr('id');
if(wJustClicked == null) alert('Id missing on item clicked');
console.log('.spinner.click! id='+wJustClicked);
var wOffset = $('#' + wJustClicked).offset();
var xPos = Math.round(wOffset.left) + 8;
var yPos = Math.round(wOffset.top) + 4;
console.log(wJustClicked+' offset left='+wOffset.left+' top='+wOffset.top+' xPos='+xPos+' yPos='+yPos);
wDiv = 'loading';
$('#'+wDiv).css('left',xPos);
$('#'+wDiv).css('top',yPos);
});
and the js function:
function _setupAjax() {
$.ajaxSetup({
beforeSend: function() {
$('#loading').show();
},
complete: function() {
$('#loading').hide();
}
});
}
A strange thing remained (I have firebug installed), which I have solved with Math.round: the x and y position come overdetailed like 170.5134577 and 434.8768664 ?!?
I can live with that. But where does this pseudo precision come from?
Again thanks a lot to keep my hope upright.
I have the following script I've written.
<script>
$(document).ready(function(){
$('a').data('loop',true);
$('body').on('click', 'a', function(event){
console.log($(this).data('loop'));
if ($(this).data('loop') == 'true') {
console.log('hit');
event.preventDefault();
caller = $(this);
$(this).data('loop',false);
var linkref = $(this).attr('href');
var linkpos = $(this).offset();
var screenwidth = $(window).width();
var json_data = JSON.stringify({linkref: linkref, linkpos: linkpos, screenwidth: screenwidth});
$.ajax({
url: "content/submitcontenthandler?handler=core/_dashboard&method=tracking_ping",
method: "POST",
data: "json=" + json_data,
complete: function (jqXHR, status) {
console.log(status);
console.log(caller);
$(caller).click();
}
});
} else {
console.log(event.isDefaultPrevented());
console.log('miss');
$(this).data('loop',true);
}
});
});
</script>
It works, sends me the details I want etc etc. BUT!!!
When I click a link, It fires off the details to me via Ajax, then it's meant to "click" the event again, which it does! but the event does not fire it's normal action. So When clicking a link to another page, I would go to that other page... that's not happening.
If I comment out the line event.preventDefault(); Then the event fires as I would expect...
So to me it looks like the event.preventDefault is executing even though it's not meant to be during the second call...
Sorry if this is a bit complicated to understand. I don't quite understand what's happening myself.
Is it possibly a bug, or is there something that I've done that has caused this?
I didn't think I could, but I have successfully made a jsfiddle for this.
https://jsfiddle.net/atg5m6ym/2001/
You can try this and not worry about the "loop" anymore:
$(document).ready(function () {
$('body').on('click', 'a', function (event) {
event.preventDefault();
var caller = $(this);
var linkref = $(this).attr('href');
var linkpos = $(this).offset();
var screenwidth = $(window).width();
var json_data = JSON.stringify({linkref: linkref, linkpos: linkpos, screenwidth: screenwidth});
$.ajax({
url: "content/submitcontenthandler?handler=core/_dashboard&method=tracking_ping",
method: "POST",
data: "json=" + json_data,
complete: function (jqXHR, status) {
console.log(status);
console.log(caller);
window.location.href = linkref; // Redirect happens here
}
});
});
});
UPDATE
There's a few issues to note here:
1) Some links don't require a redirect (as noted, bootstrap model links that control showing/hiding or within document anchors
To correct this it really depends on the case. Usually bootstrap adds specific classes or data attributes to the links so you can do something like.
$('body').on('click', 'a:not(list of things to exclude)'..
Personally I'd instead define the links I wanted to track as :
<a href=<link> data-tracked='true'...
<script>
$('body').on("click","a[data-tracked='true']"...
Or if you want to track most links with a few exceptions you can:
<a href=<link> data-tracked='false'...
<script>
$('body').on("click","a:not([data-tracked='false'])"...
Or more generally:
<script>
$('body').on("click","a", function () {
if ($(this).attr("data-tracked") == "false" || <you can check more things here>){
return true; //Click passes through
}
//Rest of the tracking code here
});
The following if statement will return true whenever the data-loop attribute exists against an element, regardless of it's value:
if ($(this).data('loop')) {
It needs to be changed to check for the value:
if ($(this).data('loop') == 'true') {
When you assign anything to be the value of an element attribute it becomes a string and, as such, requires a string comparison.
Event.preventDefault() is not being executed second time.
Link redirection happens when the method is completed.
So in your case redirection will happen when complete method of ajax call is completed.
lets say, we have event1 and event2 object in the code. event1 is the object in the ajax call method and event2 is the event object in recursive call (second call) method.
so when link is clicked second time , we still have complete method to be executed. as soon as it returns to the complete method of ajax call, it finds the event1 is having preventDefault property true and it does not redirect.
Try this ;)
$(document).ready(function(){
$('body').on('click', 'a', function(event){
event.preventDefault();
var caller = $(this);
var linkref = $(this).attr('href');
var linkpos = $(this).offset();
var screenwidth = $(window).width();
var json_data = JSON.stringify({
linkref: linkref,
linkpos: linkpos,
screenwidth: screenwidth
});
$.ajax({
url: "content/submitcontenthandler?handler=core/_dashboard&method=tracking_ping",
method: "POST",
/* To temprary block browser; */
async: false,
data: "json=" + json_data,
complete: function(jqXHR, status){
/* add class **ignore** to a element you don't want to redirect anywhere(tabs, modals, dropdowns, etc); */
if(!caller.hasClass('ignore')){
/* Redirect happens here */
window.location.href = linkref;
}
}
});
});
});
When I use this code it bind my $.post() to a form and prevent a page reload for submitting it:
$("#analyze_audio").submit(function (e) {
debugger;
e.preventDefault();
var url = "/analyze_audio";
var dataToSend = {"analyze_audio": "analyze_audio"};
var callback = function (data) {
debugger;
document.getElementById("audioresponse").innerHTML = (data);
};
$.post(url, dataToSend, callback, 'html');
});
But it doesn't trigger the debuggers I used in it, so it doesn't bind the event to my function correctly, but when I use this code fragment, it works perfectly:
$(function() {
$("#analyze_audio").submit(function (e) {
debugger;
e.preventDefault();
var url = "/analyze_audio";
var dataToSend = {"analyze_audio": "analyze_audio"};
var callback = function (data) {
debugger;
document.getElementById("audioresponse").innerHTML = (data);
};
$.post(url, dataToSend, callback, 'html');
});
});
I really don't understand why?
When you're working with jQuery (which you clearly are), wrapping a function in $( ) makes it a function that's called by the library when the "DOM ready" event is received from the browser. It thereby defers execution of your code until the DOM is fully built. That makes your code work because it ensures that the element with id "analyze_audio" is present.
There's nothing syntactically special about $( ) — it's just a simple function call, to a function named $ (which is the main entry point to the jQuery library).
You may see code that does something similar:
$(document).ready(function() { ... });
That does precisely the same thing (and is also a jQuery idiom). There's no reason to use that form unless you enjoy typing in extra characters.
$(function() {}); is just a shortcut for document.ready. It would work the same like this:
<script type="text/javascript">
$(document).ready(function() {
$("#analyze_audio").submit(function (e) {
debugger;
e.preventDefault();
var url = "/analyze_audio";
var dataToSend = {"analyze_audio": "analyze_audio"};
var callback = function (data) {
debugger;
document.getElementById("audioresponse").innerHTML = (data);
};
$.post(url, dataToSend, callback, 'html');
});
});
</script>
When you bind event to the form #analyze_audio it not present in DOM yet. If you put your script after the html with form, then it will work. Or you can use $(document).ready() to add your binding or just $(function(){}) both this functions will be executed when whole page will be loaded.
I have a problem trying to show loading icon when collapsible block is opening. I have a collapsible block with a listview inside which is populated dynamically via ajax/php. They list might have up to 500 elements, so I would like to show loading animation while it is loading.
I have tried
$('div.century').live('expand', function(){
var idval = $(this).attr('id');
console.log('expanded'+idval);
$.mobile.showPageLoadingMsg ();
$.get("helpers/getByCentury.php", { id: idval},
function(data){
$("#"+idval+" ul.ulist").html(data);
$("#"+idval+" ul.ulist").listview('refresh');
});
$.mobile.hidePageLoadingMsg ();
});
I have also tried
$('div.century').live('expand', function(){
var idval = $(this).attr('id');
console.log('expanded'+idval);
$.mobile.pageLoading();
$.get("helpers/getByCentury.php", { id: idval},
function(data){
$("#"+idval+" ul.ulist").html(data);
$("#"+idval+" ul.ulist").listview('refresh');
});
$.mobile.pageLoading(true);
});
without any luck.
Can anyone tell me how to fix this?
Thanks in advance.
You want to call $.mobile.hidePageLoadingMsg() in the callback function for your ajax call:
$('div.century').live('expand', function(){
var idval = $(this).attr('id');
console.log('expanded'+idval);
$.mobile.showPageLoadingMsg ();
$.get("helpers/getByCentury.php", { id: idval},
function(data){
$("#"+idval+" ul.ulist").html(data);
$("#"+idval+" ul.ulist").listview('refresh');
$.mobile.hidePageLoadingMsg ();//NOTICE: this has been moved inside the callback function for your $.get() call
});
});
Also a couple pointers.
You are using the $("#"+idval+" ul.ulist") selector twice in a row, you can make that more efficient by chaining function calls together like so:
$("#"+idval+" ul.ulist").html(data).listview('refresh');
If other people view your webpage in a browser that does not have the console.log function they will get an error and your JS will stop running, it is normally a good idea to put calls to the console.log function inside a conditional that checks for the existance of that function:
if (typeof(console.log) == 'function') {
console.log('expanded'+idval);
}
$(document).ready(function() {
$(".delete_user_button").click(function(){
var username_to_delete = $(this).attr('rel');
$.ajax({
type:"POST",
url:"/delete/",
data:{'username_to_delete':username_to_delete},
beforeSend:function() {
$(this).val("Removing...");
},
success:function(html){
$("div.delete_div[rel=" + username_to_delete + "]").remove();
}
});
return false;
});
});
Why doesn't $(this).val() work?
I'm trying to change the text of the button when the user clicks remove.
In your event handler (beforeSend), this refers to the XMLHttpRequest object used for the ajax call, not your original this of the click event handler. You should "capture" it in a variable first:
$(document).ready(function() {
$(".delete_user_button").click(function(){
var element = $(this);
var username_to_delete = element.attr('rel');
$.ajax({
type:"POST",
url:"/delete/",
data:{'username_to_delete':username_to_delete},
beforeSend:function() {
element.val("Removing...");
},
success:function(html){
$("div.delete_div[rel=" + username_to_delete + "]").remove();
}
});
return false;
});
});
This mechanism is called "closures". For an interesting explanation of this, check this link:
http://www.bennadel.com/blog/1482-A-Graphical-Explanation-Of-Javascript-Closures-In-A-jQuery-Context.htm
Without more knowledge about the context or analysing the script itself: Keep in mind that, in certain environments, it might be possible that $ itself does not work and needs to be replaced with jQuery - I've seen this in Liferay.
I guess this is not your problem here, but it might come in handy for others looking for this problem from another context.