Impossible to access a function out of an event - javascript

I'm quite new with javascript and I don't understand this problem:
$(function() {
var $tab_title_input = $( "#tab_title"),
$tab_content_input = $( "#tab_content" );
var tab_counter = 0;
var editors = {};
var tab_current = 0;
// tabs init with a custom tab template and an "add" callback filling in the content
var $tabs = $( "#tabs").tabs({
tabTemplate: "<li><a href='#{href}'>#{label}</a> <span class='ui-icon ui-icon-close'>Remove Tab</span></li>",
add: function( event, ui ) {
var tab_content = $tab_content_input.val() || "Tab " + tab_counter + " content.";
$( ui.panel ).append("<div id=\"editor" + tab_counter + "\" class=\"editor\">" + tab_content + "</div>");
adjust_size();
tab_current = ui.index;
editors[tab_current] = ace.edit("editor" + tab_counter);
},
show: function( event, ui ) {
adjust_size();
tab_current = ui.index; // zero-based index
editors[tab_current].resize();
},
select: function( event, ui ) {
adjust_size();
tab_current = ui.index; // zero-based index
},
});
The problem is that this line of code:
editors[tab_current].resize();
breaks everything telling Uncaught TypeError: Cannot call method 'resize' of undefined.
But editors editors[tab_current].resize() is well defined in the add event and alert(tab_current) gives me the correct result.

I'd bet money that editors[tab_current] returns undefined.
Your alert(tab_current) may well return a correct value, but that doesn't mean that there's an element of editors that corresponds to it. Test it with alert(editors[tab_current]), and if it shows undefined then go check if the element is being set properly.

I can see two avenues of investigation straight away:
What does ace.edit("editor" + tab_counter) return? Does it always return an object with a resize method or does it sometimes return undefined?
Is add always called prior to show for any value of tab_current?

Related

jquery class selector yields unusable id

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.

Issue with .on() method

I am trying to make an MVC for training purposes and I am following a tutorial for that which is rather old. The implementation in the tutorial was made using live() but I decided to use jQuery 2.1.1 and have to implement on() method. I made a small use case for clarification.
I can insert new elements on the page while adding them in the DB
I can delete preloaded elements which existed in the DB at page load both from the DB and DOM
I can not remove elements which are added live neither from the db nor the DOM.
this is my entire code regarding that.
$(function(){
$.get('dashboard/xhrGetListings', function(o){
for (var i = 0; i < o.length; i++ )
{
$('#listInserts').append('<div>' + o[i].text + '<a class="del" rel="' + o[i].id + '" href="#">x</a></div>');
}
$('.del').on("click", function() {
delItem = $(this);
var id = $(this).attr('rel');
$.post('dashboard/xhrDeleteListing', {'id': id}, function(o) {
delItem.parent().remove(); // THIS IS NOT EXECUTED AT ALL
}, 'json');
return false;
});
}, 'json');
//Not necesarly relevant, it just helps for code clarity
$('#randomInsert').on("submit", function() {
var url = $(this).attr('action');
var data = $(this).serialize();
console.log(data);
$.post(url, data, function(o) {
$('#listInserts').append('<div>' + o.text + ' <a class="del" rel="' + o.id + '" href="#">X</a></div>');
}, 'json');
return false;
});
});
Another issue that I'm not focussing on at this point is that if I delete the parent inside the $.post method (as shown in the code above) it's not deleted, only if I move that line outside of the post method. Any clarification on that would be also very appreciated.
Use event delegation and event.preventDefault() it stops the default action
$('#listInserts').on("click", '.del' , function(event) {
event.preventDefault();
// your code come here
});

jQuery: event not being triggered on ajax-loaded elements [duplicate]

This question already has answers here:
Events triggered by dynamically generated element are not captured by event handler
(5 answers)
Closed 8 years ago.
I have a simple front-end in jQuery/HTML5 (+ backend-generated code which does not bring the issue, so I will omit it). The currently-in-use jQuery version is 1.8.3 and no version collision exists (i.e. no other jQuery version is loaded - it happened many times in other systems here).
The front-end invokes the following routines:
detailAjaxCall("\/client\/orders\/detailsLoad\/id\/3");
$(".upLink").click(function(){
console.log("subiendo");
var id = $(this).closest("tr").data('detail-id');
var url = "\/client\/orders\/detailMoveUp" + "/id/" + id;
detailAjaxCall(url);
return false;
});
$(".downLink").click(function(){
console.log("bajando");
var id = $(this).closest("tr").data('detail-id');
var url = "\/client\/orders\/detailMoveDown" + "/id/" + id;
detailAjaxCall(url);
return false;
});
$(".delLink").click(function(){
console.log("borrando");
var id = $(this).closest("tr").data('detail-id');
var url = "\/client\/orders\/detailDelete" + "/id/" + id;
detailAjaxCall(url);
return false;
});
Note: the url string are not malformed. they are generated by a json exporter (this chunk of code was extracted from the view source option in Google Chrome browser). Evaluating any of them will return a string with no backslashes.
The detailAjaxCall("/client/orders/detailsLoad/id/<number>") actually works: it returns the expected json code when I hit the url, and renders the appropiate table items:
function detailAjaxCall(url)
{
$.get(
url,
{},
function(data, status, xhr) {
//todo refrescar(data);
var table = $("#detail-list");
table.empty();
if (data.length == 0) {
$("<tr></tr>").addClass("empty").append($("<td></td>").addClass("empty").text("No hay detalles para este pedido")).appendTo(table);
} else {
$.each(data, function(index, element) {
$("<tr></tr>")
.data('detail-id', element['id'])
.append(
$("<td></td>")
.append(
$("<span></span>").addClass("product-name").text(element['producto_nombre'])
)
.append("<br />")
.append(
$("<span></span>").addClass("product-dims").text(
"Ancho: " + element['ancho'] +
", Largo: " + element['largo'] +
", Calibre: " + element['calibre']
)
)
)
.append($("<td></td>").addClass("quantity").text(element['cantidad']))
.append($("<td></td>").addClass("price").text(element['precio']))
.append(
$("<td></td>")
.append(
$("<a></a>").addClass("upLink").text("subir").attr("href", "javascript: void 0")
).append(" ")
.append(
$("<a></a>").addClass("downLink").text("bajar").attr("href", "javascript: void 0")
).append(" ")
.append(
$("<a></a>").addClass("delLink").text("eliminar").attr("href", "javascript: void 0")
).append(" ")
)
.appendTo(table);
});
}
},
'json'
).fail(function(){
$("#ajaxDetailErrorDialog").dialog("open");
});
}
Pay attention to the generation of the "<a></a>" since my problem is with them. They all have classes like delLink, upLink and downLink.
My issue starts here: calling $(".delLink").click(callback), $(".upLink").click(callback), $(".downLink").click(callback) does not seem to bind the events to the newly created items (althought they are created inside the ajax call). Seeing the source code for the click method, passing parameters, is like a call to on.
So: what am I doing wrong to bind the event dynamically, so newly created elements trigger my events as well?
You need to dynamically delegate the click handlers because you assign your click handlers before the new elements are created.
For example, delegate to the document:
$(document).on('click', '.upLink', function(){
console.log("subiendo");
var id = $(this).closest("tr").data('detail-id');
var url = "\/client\/orders\/detailMoveUp" + "/id/" + id;
detailAjaxCall(url);
return false;
});
This works because all clicks on the document will be checked by this handler, to see if they match .upLink. Even if you create new elements after this is assigned, the clicks still pass through this event.

Generate input handler via jQuery for xmlhttprequest json data in a table

I'm struggling with dynamic generation of buttons over a JSON array.
Stripped-down code is this (aim is to build a table based on the data, nothing fancy yet, I'm not yet proficient at this):
$.ajax({
/* type, content, etc. removed */
success: function (data, textStatus, XmlHttpRequest) {
var target = $('myContainerDiv');
var result = data.d.results;
var $table = $('<table />');
for(var i=0;i < results.length; i++) {
var $row = $('<tr />');
var $cell = $('<td />');
var $button = $('<input />').attr({ type: 'button', value: 'Edit', name: 'btn' + i });
$button.click(function () {
// **
// In a .NET environment, this would become a closure
// I suspect this is the offending bit of code
//
alert(results[i].name);
};
$cell.append($button);
$row.append($cell);
$table.append($row);
}
$target.append($table);
},
/* error etc. removed*/
});
I basically want a column filled with buttons, each one would popup the value of a field from the array I get from my $.ajax call.
Buttons actually show up, but they do not react to clicking, and I see no runtime error in the F12 tools console. This is probably due to the fact that this script is part of the configuration page for a Microsoft Dynamics CRM 2011 Solution, but other than that, I'm sure the AJAX call goes on OK (I tried making it print out data, and I can see it).
UPDATE
Referencing i inside the click handler was the offending line indeed: changed the code like this made things work as I was expecting:
var $button = $('<input />').attr({ type: 'button', value: 'Edit', name: 'btn' + results[i].name });
$button.click(function () {
// 'i' value is NOT what I thought it was !
alert(this.name.substring(3,this.name.length));
// I found out in the meanwhile that 'this' references the event source
};
First you have several syntax errors in your code and it may not be running at all:
if the ID of your div container is myContainerDiv, to get the target you need to do $('#myContainerDiv')
you create a result varialbe, but you use a results variable
you're not closing the parentesis in the $button.click
you're adding everything to $target but is defined as target
Now the actual problem may be, as you say, the closure, remember that you close over variables not values, so when you execute the button click handler, i has a value of results.length, so you are out of bounds by that time.
You could try to store the results objects elsewhere, extract the Id of the object your looking for from the button (you're naming then 'btn'+i) and then access the name property that way.
I noticed you didn't close the .click() bracket.
Try...
$button.click(function () {
alert(results[i].name);
});
You can try this ...
$.ajax({
/* type, content, etc. removed */
success: function (data, textStatus, XmlHttpRequest) {
var $target = $('#myContainerDiv'),
results = data.d.results,
$table = $target.append('<table />').children('table:last-child'), $trSet = $([]);
for(var i=0; i < results.length; i++) {
$trSet = $trSet.add(
$([
'<tr><td>',
$('<input type="button" value="edit" name="btn'+i+'" rel="'+results[i].name+'" />').wrap('<div />').html(),
'</td></tr>'
].join(''))
);
}
// Using Event delegation ... Only one handler is attached to the DOM.
$table.append($trSet).click(function(e){
var $t = $(e.target);
if( $t.is('input[type="button"]') ){
alert($t.attr('rel'));
}
// choose to return false whether to prevent event bubbling up the DOM or not
// return false;
});
},
/* error etc. removed*/
});

How to overwrite this.href in the beforeShow block? (jquery / fancybox)

I'm trying to overwrite this.href in the beforeShow block but it doesn't work.
The old value of this.href is being used even if it's overwritten.
Code is:
beforeShow: function (opts, fb_obj) {
// this conditional is some other code checking $(window).width() and $(window).height() and
// if the bigger image will fit; it is simplified here!
if (1) {
var source = this.href;
this.href = source.replace('_large','_super_large');
console.log('retina detacted! ' + source + " " + this.href);
// console output is OK, but this.href is not beeing replaced in the output!
}
If you want to override the value of this.href, use beforeLoad instead.
NOTE: this is for fancybox v2.0.6+
Since you tagged this in jquery ..ill say
The full code
if (1) {
var oldhref = $(this).attr('href');
$(this).attr('href','_super_large');
console.log('retina detacted! ' +oldhref + " " + this.attr('href');
}

Categories

Resources