Jquery - custom HTML for template - strange behavior - javascript

var $template;
$.get('/templates/searchTemplate.tmpl', function(HTMLres) {
$template = $(HTMLres);
});
$.each(data.results, function(key, value) {
$template.find('button').off().on('click', function() { console.log('you clicked me') });
$template.find('.media-body > strong').html(value.firstname + ' ' + value.lastname);
$template.find('.email').html(value.mail);
$template.find('.phone').html(value.phone);
$template.find('.age').html(value.age);
$('#searchRsults').append($template);
});
When I try to use this code instead of having the template file to be Appended to the UL element, this will clear the previous append and append the next result to the UL element.
The template file is just a -> <LI> </LI> with some spans.
Note that if I put the |$.get (template) in the $.each part of the code everything works like it supposed to be all the <LI> </LI> element are appended one after another.
But doing so it makes a lot of request for the template file. On every iteration actually.
Even if I use the .DONE after the Ajax request still the result is the same the <LI> </LI> gets overwritten not appended

You should put the each() inside the success callback so the $template will be available, else you're trying to use the $template when it's not returned yet from the asynchronous HTTP (Ajax) request :
var $template;
$.get('/templates/searchTemplate.tmpl', function(HTMLres) {
$template = $(HTMLres);
$.each(data.results, function(key, value) {
$template.find('button').off().on('click', function() { console.log('you clicked me') });
$template.find('.media-body > strong').html(value.firstname + ' ' + value.lastname);
$template.find('.email').html(value.mail);
$template.find('.phone').html(value.phone);
$template.find('.age').html(value.age);
$('#searchRsults').append($template);
});
});
Take a look to How do I return the response from an asynchronous call?.
Hope this helps.

function getResults(searchTerm){
return $.get("/api/search", { 'searchFor': keyword });
}
function getTemplate(){
return $.get('/templates/searchTemplate.tmpl')
}
$.when(getResults(keyword), getTemplate())
.done(function(result1, result2){
$('ul#searchRsults').empty();
if (result1[0].results.indexOf('no results') != 0) {
$.each(result1[0].results, function(key, value) {
var $template = $(result2[0]);
$template.find('.img-avatar').html(img);
$template.find('button').off().on('click', function() { console.log('you clicked me') });
$template.find('.media-body > strong').html(value.firstname + ' ' + value.lastname);
$template.find('.email').html(value.mail);
$template.find('.phone').html(value.phone);
$template.find('.age').html(value.age);
$('#searchRsults').append($template);
});
}
})
after some read - I ended with this solution, in chrome console the request for template is made only once. Hope this helps sombady

Related

Bind an event on a jquery generated element

My code is like this:
$('.flag-icon-dz').click(function() {
var lang = 'Arab';
var $frame = $('.goog-te-menu-frame:first');
if (!$frame.size()) {
console.log("Error: Could not find Google translate frame.");
return false;
}
$frame.contents().find('.goog-te-menu2-item span.text:contains(' + lang + ')').get(0).click();
$("li.ql-item.linkid188546").after("<li class='ql-item linkid18854777 closegoogle'><a href='#' class='btn-primary' target='_self'><i class='icon closegoogle ls-lang-frr' aria-hidden='true'></i></a></li>").fadeIn(500);
$('li.ql-item.linkid188546').fadeOut(500);
return false;
});
$('.closegoogle').click(function() {
$('.skiptranslate').contents().find('.goog-close-link > img').click();
$('li.ql-item.linkid18854777').fadeOut('fast').remove();
$('li.ql-item.linkid188546').fadeIn(500);
});
The first function works great, but the second doesn't. I realize that if I copy/paste the second function in the console after the first one, it works too.
I tried a few solutions (callback / setTimeout / jquery deferred / jquery .when method...) I didn't try promise but I don't think I have to in my context. Maybe I didn't write these solutions good enough.
I finally try to put my event (click) directly the .before() which create my new element like this :
$('.flag-icon-dz').click(function() {
var lang = 'Arab';
var $frame = $('.goog-te-menu-frame:first');
if (!$frame.size()) {
console.log("Error: Could not find Google translate frame.");
return false;
}
$frame.contents().find('.goog-te-menu2-item span.text:contains(' + lang + ')').get(0).click();
$("li.ql-item.linkid188546").after("<li class='ql-item linkid18854777 closegoogle'><a href='#' class='btn-primary' target='_self'><i class='icon closegoogle ls-lang-frr' aria-hidden='true'></i></a></li>").fadeIn(500).click(function() {
$('.skiptranslate').contents().find('.goog-close-link > img').click();
$('li.ql-item.linkid18854777').fadeOut('fast').remove();
$('li.ql-item.linkid188546').fadeIn(500);
});
$('li.ql-item.linkid188546').fadeOut(500);
return false;
});
But it doesn't work either.
Thanks for the help.
EDIT :
I finally found a kind of solution for my second click event (which isn't the best solution but i works) :
window.setInterval(function(){$('.closegoogle').on("click",function(){
$('.skiptranslate').contents().find('.goog-close-link > img').click();
$('li.ql-item.linkid18854777').fadeOut('fast').remove();
$('li.ql-item.linkid188546').fadeIn(500);
}); }, 1000);
Thanks.
You need to use a delegated bind as the element does not exist before you try your binding:
$('#parent-element-of-closegoogle').on('click', '.closegoogle', function() {
$('.skiptranslate').contents().find('.goog-close-link > img').click();
$('li.ql-item.linkid18854777').fadeOut('fast').remove();
$('li.ql-item.linkid188546').fadeIn(500);
});
Please note that the #parent-element-of-closegoogle needs to be an element that already exists when you do the binding - this can be $(document) if you hjave no other element to bind to

e.preventDefault does still loads anchor link in a new tab in browser

I have basically a ajax call that invokes a REST API that gives me list of all names and I have another REST API that matches that. For example,
/list gives me: list1,list2,list3
and
/api/list1.json gives me: json of list1..
But I have my code where I loop through all the lists and invoke /api/list1.json
I want that JSON to be displayed in a div when a onclick event occurs by grabbing the href accordingly without page reload. But right now, since that is also a valid link browser just takes me there.
Here is my code:
$(function() {
$.ajax({
dataType: 'json'
url: '/lists',
success: function (data) {
if (data != null) {
var html = '<ul>';
$.each(data.apis, function (i, item) {
//click event
$('a').click(function(e) {
e.preventDefault();
});
html += '<li class="res">';
html += '<div class="hed"><h2>' + item + '</h2></div>';
html += '</li>';
});
html += '</ul>';
$('#exDiv').empty();
$('#exDiv').append(html);
}
},
error: function () {
alert('Error');
},
contentType: 'application/json'
});
$('a').click(function(e) {
e.preventDefault();
});
});
Apparently I also added e.preventDefault() but it still triggers the link to a new tab.
Link to e.preventDefault()
These are dynamically added anchor tags. They don't exist when you add the click event handler to the anchor tags. So when you click these anchors they are going to bypass your jquery event handlers and do what they normally do by default.(further explanation) You have the same code again inside the $.each function which might have worked if you had called it after your $('#exDiv').append(html); line. But again they still don't exist when you call it.
Depending on the version of jQuery you're using you should use either "on" or "live". If you are using a version 1.7 or higher use 'on'.
Try this:
$(function() {
$.ajax({
dataType: 'json'
url: '/lists',
success: function (data) {
if (data != null) {
var html = '<ul>';
$.each(data.apis, function (i, item) {
html += '<li class="res">';
html += '<div class="hed"><h2>' + item + '</h2></div>';
html += '</li>';
});
html += '</ul>';
$('#exDiv').empty();
$('#exDiv').append(html);
}
},
error: function () {
alert('Error');
},
contentType: 'application/json'
});
$(document).on('click', 'a', function(e) {
e.preventDefault();
});
});
If you're using 1.6 or ealier your click event handler should look like this:
$('a').live('click', function(e) {
e.preventDefault();
});

Unobtrusive jQuery Set Attributes Before Append - Is More Effecient Syntax Possible?

The basic process for this code is:
$.get a Mustache template
$.getJSON some data from the API
Then the code below to process on the client
$.each(data, function (i, item) {
item.Date = internationalDateTime(item.Date );
var html = Mustache.to_html(template, item);
var $html = $(html);
$html.find('input[name=btnView]').attr('onclick', 'loadView(' + item.Id + ')');
$('#list-container').append($html);
});
Is there a better way (read more succinct I guess) of doing this without the need for the $html variable?
Not wanting to obsess over "one liners" in my code but it just looks a bit wrong to me.
before adding your html to the DOM, you can do this:
$(document).on("click", "#list-container input[name=btnView][itemId]", function(){
loadView($(this).attr("itemId"));
});
then just add your html to the page like this, the only point here is to store the item.Id:
$.each(data, function (i, item) {
item.Date = internationalDateTime(item.Date );
$(Mustache.to_html(template, item)).appendTo('#list-container')
.find("input[name=btnView]").attr("itemId", item.Id);
});
your problem this way is just to get access to item.Id.
You can trim it down, but for readability I normally do the exact type of thing you've posted...
$.each(data, function (i, item) {
item.Date = internationalDateTime(item.Date );
var html = Mustache.to_html(template, item);
$(html)
.appendTo('#list-container')
.find('input[name=btnView]')
.on('click', function() {
loadView(item.Id);
});
});
You could also get rid of the html variable, if you really wanted to, but I think you can go too far with minimizing code.
appendTo() will return the html as a jQuery object so you can find the input elements specified, and then attach a click event handler, all in one hit.
What's about with:
$.each(data, function (i, item) {
item.Date = internationalDateTime(item.Date);
var html = Mustache.to_html(template, item);
html.querySelector('input[name=btnView]').addEventListener('click', function() {
loadView(item.Id);
}, false);
$('#list-container').append(html);
});

Apply a jQuery function live() in 1 object

I have a Facebook-Like Chat. (You can see it # http://www.live-pin.com/). It gets the last messages from a JSON file and inserts into an individual UL for each user, it before checks if the ul exists and if it doesnt, it creates. Now the problem is that when I click on 1 chat bar, the 3 open at the same time, and only close if I click on the last one, what can I do? I want that this bars only open/close when clicked on chat bar but doesnt if click on not_clickable. Thanks for your help
$(document).ready(function(){
getOnJSON();
setInterval("getOnJSON()", 60000);
var Id;
var target;
});
function showChat(obj){
$(obj).animate({marginBottom : "0"}).removeClass("hidden_box").addClass("active_box").unbind('click')/*.click(function(){
hideChat(this);
})*/;
}
function hideChat(obj){
$(obj).animate({marginBottom : "-270px"}).removeClass("active_box").addClass("hidden_box").unbind('click')/*.click(function(){
showChat(this);
})*/;
}
function getOnJSON(){
var self = this; // Added this line, as this changes scope in each()
var from;var to;var msg_id;var msg_txt;var new_chat_string;
//Getting the data from the json file
$.getJSON("/ajax/chat.json.php",function(data){
$.each(data.notif, function(i,data){
from = data.from;to = data.to;msg_id = data.id;msg_txt = data.text;
if ($("#chat_"+from+"_lp").length === 0){
$("#boxes").append('<div id="chat_'+from+'_lp" class="chat_box hidden_box clickable_box"><div id="chat_'+from+'_nick" class="chat_name">'+from+'</div><div class="not_clickable"><ul id="chat_'+from+'_txt" class="chat_txt"><li id="' + msg_id + '">'+ msg_txt+'</li></ul><form class="chat_new_message" name="new_msg"><input type="text" placeholder="Enter your message..." class="chat_new_input"/></form></div></div>');
$('.hidden_box #chat_'+from+'_nick').live("click", function(){ showChat('#chat_'+from+'_lp'); });
$('.active_box #chat_'+from+'_nick').live("click", function(){ hideChat('#chat_'+from+'_lp'); });
}else{
$("#chat_"+from+"_txt").append('<li id="' + msg_id + '">'+ msg_txt+'</li>');
$('.hidden_box #chat_'+from+'_nick').live("click", function(){ showChat('#chat_'+from+'_lp'); });
$('.active_box #chat_'+from+'_nick').live("click", function(){ hideChat('#chat_'+from+'_lp'); });
}
});
});
}
You need to use jquery .live() function for your .click() to be applied to elements created after the document load complete.
For instance: $("a.offsite").live("click", function(){ alert("Goodbye!"); }); is an example from here

Why does my jQuery event handler fail when attached to multiple elements?

I am using jquery to add mulitple new "addTask" form elements to a "ul" on the page every time a link is clicked.
$('span a').click(function(e){
e.preventDefault();
$('<li>\
<ul>\
<li class="sTitle"><input type="text" class="taskName"></li>\
<li><input type="button" value="saveTask" class="saveTask button"></li>\
</ul>\
</l1>')
.appendTo('#toDoList');
saveTask();
});
These new nested ul elements all have an button with the same class "saveTask". I then have a function that allows you to save a task by clicking on an button with the class "saveTask".
// Save New Task Item
function saveTask() {
$('.saveTask').click(function() {
$this = $(this);
var thisParent = $this.parent().parent()
// Get the value
var task = thisParent.find('input.taskName').val();
// Ajax Call
var data = {
sTitle: task,
iTaskListID: 29
};
$.post('http://localhost:8501/toDoLists/index.cfm/Tasks/save',
data, function(data) {
var newTask = '<a>' + task + '</a>'
thisParent.find('li.sTitle').html(newTask);
});
return false;
});
}
This essentially allows the user to enter some text into a form input, hit save, and then the task gets saved into the database using ajax, and displayed on the page using jQuery.
This works fine when there is only one element on the page with the class "saveTask", but if I have more than 1 form element with the class "saveTask" it stops functioning correctly, as the variable "var task" shows as "undefined" rather than the actual value of the form input.
Don't rely on the .parent() method. Use .closest('form') instead. So the following line:
var thisParent = $this.parent().parent()
should look something like this instead:
var thisParent = $this.closest('form');
EDIT:
Based on the updated information you provided, it looks like when you're trying to register the click event handler it's failing out for some reason. Try this javascript instead as it will make use of the live event so that all the newly added items on the page will automatically have the click event autowired to them.:
$(function(){
$('span a').click(function(e){
e.preventDefault();
$('<li>\
<ul>\
<li class="sTitle"><input type="text" class="taskName"></li>\
<li><input type="button" value="saveTask" class="saveTask button"></li>\
</ul>\
</l1>')
.appendTo('#toDoList');
});
$('.saveTask').live('click', function() {
$this = $(this);
var thisParent = $this.closest('ul');
// Get the value
var task = thisParent.find('input.taskName').val();
// Ajax Call
var data = {
sTitle: task,
iTaskListID: 29
};
$.post('http://localhost:8501/toDoLists/index.cfm/Tasks/save',
data, function(data) {
var newTask = '<a>' + task + '</a>'
thisParent.find('li.sTitle').html(newTask);
});
return false;
});
});
First turn the save task into a function:
(function($){
$.fn.saveTask= function(options){
return this.each(function(){
$this = $(this);
$this.click(function(){
var thisParent = $this.parent().parent()
//get the value
var task = thisParent.find('input.taskName').val();
// Ajax Call
var data = {
sTitle: task,
iTaskListID: 29
};
$.post('http://localhost:8501/toDoLists/index.cfm/Tasks/save', data, function(data){
var newTask = '<a>' + task + '</a>'
thisParent.find('li.sTitle').html(newTask);
});
});
});
return false;
})(jQuery)
When the app starts change the saveTask selector to this:
function saveTask(){
$('.saveTask').saveTask();
}
Then on your add function:
function addTask(){
$newTask = $("<div>Some Task stuff</div>");
$newTask.saveTask();
}
This code is written very quickly and untested but essentially create a jQuery extension that handles for data submission then when ever a task is created apply the save task extension to it.
I think you're looking for the live event.
Also, your code is a little awkward, since the click event is only added when the saveTask() function is called. In fact, the saveTask() function, doesn't actually save anything, it just adds the click event to the elements with the .saveTask class.
What is your HTML structure?
It looks like your code can't find the input.taskName element.
Try setting thisParent to something like $this.closest('form'). (Depending on your HTML)
You could try wrapping your click function in an each()
ie
function saveTask(){
$('.saveTask').each (function () {
$this = $(this);
$this.click(function() {
var thisParent = $this.parent().parent()
//get the value
var task = thisParent.find('input.taskName').val();
// Ajax Call
var data = {
sTitle: task,
iTaskListID: 29
};
$.post('http://localhost:8501/toDoLists/index.cfm/Tasks/save', data, function(data){
var newTask = '<a>' + task + '</a>'
thisParent.find('li.sTitle').html(newTask);
});
return false;
});
})
}
I find this helps sometimes when you have issues with multiple elements having the same class

Categories