Why we use $(function() { }) like syntax in JS? - javascript

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.

Related

JQuery conditional preventDefault() firing when not called

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

Losing scope in jQuery AJAX Lightbox

My lightbox opens and the new content is displayed within it.
But the script doesn't seem to add the event listener to the links in the generated content. Thus nothing happens but a new pageload.
var lightbox = (function() {
var init = function() {
$('footer').append("<div id='lightbox'><div id='close'>x</div></div>");
$('#close').click(function() {
$('#lightbox').hide();
$('#lightbox').children(':not(#close)').remove();
});
ajax();
};
var ajax = function() {
$('.ajax a').add('#lightbox a').click(function(e) {
e.preventDefault();
$('#lightbox').children(':not(#close)').remove();
$.ajax({
url: $(this).attr('href'),
success: function(result) {
$('#lightbox').fadeIn(300).append(result);
}});
});
};
return {
init:init
}
})();
You don't actually invoke init which is where your close handler is bound. init is a local variable which is never returned from your IIFE, so obviously it's not being invoked from outside this function.
The answer is to invoke init. As it stands, your lightbox function doesn't do anything except for declare (and then immediately discard) two variables named init and ajax.

Conditional addClass not working for each elements with same class

Les say I have some buttons with same class. On page load I am checking some value using ajax for each button. Depending on returned value of ajax request I want to add some class to the buttons, but it is not working,
$(document).ready(function(){
$('.add-remove-permissoion').each(function(){
var child = $(this).val();
var parent = $('#parent-name').text();
$.get('my-url', function(data){
if(data == 1){
$(this).addClass('glyphicon glyphicon-ok');
}else{
$(this).addClass('emptybox-blank');
}
});
});
});
I have checked that my ajax request is returning correct data. What is that I am doing wrong here?
The problem is the this reference inside the ajax callback, in the success callback this refers to the jqXHR object not the dom element reference that is why it is not working.
You can use a closure variable as given below to fix the problem
$(document).ready(function () {
$('.add-remove-permissoion').each(function () {
var $this = $(this),
child = $this.val();
var parent = $('#parent-name').text();
$.get('my-url', {}, function (data) {
if (data == 1) {
$this.addClass('glyphicon glyphicon-ok');
} else {
$this.addClass('emptybox-blank');
}
});
});
});
this in the context of the $.get handler doesn't refer to the element of the current iteration. Each function has it's own this value. You have several options.
Use the second parameter of the each callback.
$('.add-remove-permissoion').each(function(index, element) {
Use $.proxy or Function.prototype.bind method for setting the this value of the handler.
$.get('my-url', function(data) {
// ...
}.bind(this));
Cache the this value of the each handler and use it in your $.get handler.
var elem = this;
$.get('my-url', function(data) {
// ...
$(elem)...
});
Also note that there is a syntax error in your code:
$.get('my-url'}, function(data){
// -----------^
Problem is $(this) within ajax call does not refer to the button clicked.
Replace $(this).addClass with myElement.addClass. Create myElement within click event just before the ajax call: var myElement = $(this).

jquery unable to show and hide an element

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();
}
});

Javascript file not executing after AJAX call

When I make an AJAX call to replace a div, the response contains a script tag pointing to an external .js file. However, I can't get the returned JS to execute. I've tried to eval() the response but that didn't work. I also tried to call a function inside the external .js file from within the onComplete callback, but this also does not work. Not sure what else to do. I'm using mootools core 1.4.5
Main page's JS
window.addEvent('domready', function(){
function ajaxfunc(i)
{
return function(e){
e.stop();
var requestData = new Request({
url: 'blah.php?cat=' + i,
evalScripts: true,
evalResponse: true,
onComplete: function(response){
$('rt-main').set('html', response);
}
});
requestData.send();
};
}
var total = $('cat_table').getChildren('div').length;
for(var i=1; i<=total; i++)
{
$('catClick'+i).addEvent('click', ajaxfunc(i));
}
});
The returned HTML
<script src="listings.js" type="text/javascript"></script>
...(other markup, etc)
And inside that listings.js file
window.addEvent('domready', function(){
function gotoItem(i)
{
return function(e){
e.stop();
var id= i;
var requestData = new Request ({
url: 'blah.php?id='+id,
onComplete: function(response){
$('rt-main').set('html', response);
}
});
requestData.send();
};
}
$$('.itemBox').each(function(el){
el.getElement('a.itemClick').addEvent('click', gotoItem(el.id));
});
});
The environment I'm working in is Joomla 3.1 in case that affects anything.
No domready will fire a second time for you as per listings.js, your DOM is already ready.
You could manually do window.removeEvents('domready') beforehand, then load via XHR and do window.fireEvent('domready') to run it.
If you use event delegation you can avoid having to run any js after initial ajax requests, all you'd need is something like this.
window.addEvent('domready', function () {
var ct = document.id('cat_table'),
divs = ct.getChildren('div'), //need a more qualified selector
rtMain = document.id('rt-main');
divs.each(function(el, i){
// store the index, if you don't have it as an attribute like id, rel or data-id
el.store('index', i);
});
ct.addEvent('click:relay(div)', function(e){ // needs more qualified also.
e && e.stop();
new Request({
method: 'get', // not post, check - faster
url: 'blah.php?cat=' + this.retrieve('index'),
evalResponse: true,
onComplete: function(){
rtMain.set('html', this.response.text);
}
}).send();
});
// delegation, assumes .itemBox are children of rtMain - just delegate to other parent otherwise.
rtMain.addEvent('click:relay(.itemBox)', function(e){
// reliance on index in collection is bad, try to change response above to contain data-id.
e.stop();
new Request({
method: 'get',
url: 'blah.php?id=' + this.get('data-id'),
onComplete: function(){
rtMain.set('html', this.response.text);
}
}).send();
});
});
keep in mind you had a reliance on the index of the item in the Elements collection, which is less than safe. use a hard db id, provided by the backend. this is insecure, ppl can mod their DOM, delete elements and get to ids they should not see. Not that they can't do so otherwise but...
Bringing in scripts via XHR and evaling responses is an anti-pattern and gives a vector of attack on your page for XSS, don't forget that.
It looks like the script in the linked .js is wrapped in a 'domready' event, but (I'm assuming a little here) the dom would already have fired the ready event. Try removing the window.addEvent('domready', function(){ ...} wrapper.
EDIT since you say that is not the case could it work for you if instead of returning the script tag, you simply returned the script and appended it to the page like this:
onComplete: function (response) {
//
var script = document.createElement('script');
script.innerHTML = response;
document.getElementById('rt-main').appendChild(script);
}
EDIT I've just played with this a little, and it seems I have an inelegant solution fiddle.

Categories

Resources