I am new at jQuery/javascript. I tried some suggestions I found on this forum but so far it did not help.
THis is what I am trying:
When loading categories from a database ( using ajax) this HTML statement is added for each category:
$("#Links ul").append('<li id=\"cat' + i + '" data-catid=' + i + '>' + categorie_tekst[1] + '</li>');
Using F12 I see that the lines are correctly added.
E.g. <li id="cat3" data-catid="3">Seafood </li>
Next step is selecting a category in the screen and retrieve the products of this category using the value set for data-catid.
I have been told that I could "this.id" but so far no luck. Displaying the value of this.id with alert returns the correct value but for some reason I can't use it.
When I add (#cat3).attr(“data-catid”) in the code it works. But different options like these did not work:
("#cat"+ this.id).attr(“data-catid”)
(this).attr(“data-catid”)
var x = $(this).id();
var rest = x.substr(4, 1);
Everything with "this" creates error : Uncaught TypeError: ("#cat" + this.id).attr is not a function...
Trying to display any of these options does not give any output (not even a popup when I set an alert)
Any help would be appreciated!
You are loading dynamic values. Please use Event Delegation. And the $.one() binds the event once.
You need to add this in your ready function.
$(document).ready(function () {
$("#Links ul").one("click", ".cat", function(){
alert($(this).data('catid'))
});
});
To get the IDs of the elements, use $(this).attr("id") or $(this).prop("id") (latest jQuery) instead of this.id, as sometimes, it might return the jQuery object.
As you are creating elements like
$("#Links ul").append('<li class="cat" id=\"cat' + i + '" data-catid=' + i + '>' + categorie_tekst[1] + '</li>');
create elements using jQuery
$("#Links ul").append( $('<li></li>', {
class: "cat",
id: "cat" + i,
data-catid: i,
text: categorie_tekst[1]
});
As you are creating elements dynamically use Event Delegation. You have to use .on() using delegated-events approach.
$(document).ready(function () {
$("#Links ul").on(event, ".cat", function(){
alert($(this).data('catid'))
});
});
Related
Since I want to use classes instead of id's in these functions(I have three of the same function with different things I want to .append) I am sure I need to put $(this) in those functions somewhere to only trigger only ONE function on button click and not all three of them. but I am not sure because I am a total beginner in jquery/js, so I would appreciate some help.
$(document).ready(function () {
$(".onclick").click(function () {
$('#favorites').append('<div data-role="main"class="ui-content"><div class="ui-grid-b"><div class="ui-block-a">Arrow</div><div class="ui-block-b">More Info</div><div class="ui-block-c">Unfavorite</div></div></div>');
});
});
http://codepen.io/anon/pen/JYxqEw - HTML And the Jquery Code
$('.onclick') selects all the elements with a class of onclick. That means that, whenever something with class="onclick" is clicked, that function will fire.
If you want all of those elements to append that exact HTML to the #favorites element, then you can leave your code as-is.
However, if what you're trying to do is append that html to the clicked element, that is when you'd use $(this) -- that selects the element you clicked with jQuery, then you can append directly to that element ie:
$(document).ready(function () {
$(".onclick").click(function () {
// this will append the HTML to the element that triggered the click event.
$(this).append('<div data-role="main"class="ui-content"><div class="ui-grid-b"><div class="ui-block-a">Arrow</div><div class="ui-block-b">More Info</div><div class="ui-block-c">Unfavorite</div></div></div>');
});
});
EDIT
so to insert the contents of each .onclick into #favorites, you'll need to use the innerHTML value of the DOM node. example fiddle:
http://jsbin.com/qazepubuzu/edit?html,js,output
When you select something with jQuery, you're actually getting back not just the DOM node, but a jQuery object -- this object contains both a reference to the actual DOM node ([0]), as well as a jquery object ([1]).
So to select the DOM node with $(this), you target the node: $(this)[0]. Then you can use .innerHTML() to grab the HTML contents of the node and do as you like.
Final result:
$(function () {
$('.onclick').click(function () {
$('#favorites').append( $(this)[0].innerHTML );
});
});
So the building blocks are not that complex, but I think you're a novice jQuery developer and so you may not be clear on the difference between jQuery and JS yet.
$(selector, context) allows us to create a jQuery collection for a CSS selector which is the child of a current context DOM node, though if you do not specify one there is an automatic one (which is document.body, I think). Various functions iterating over jQuery collections make the particular element available as this within the JavaScript. To get to the strong element from the .onclick element in the HTML fragment you need to travel up in the hierarchy, then to the appropriate element. Then, we can collect the text from the element. We can do this in either JS or jQuery.
To do this with simply jQuery:
// AP style title case, because Chicago is too crazy.
var to_title_case = (function () { // variable scope bracket
var lower_case = /\b(?:a|an|the|and|for|in|so|nor|to|at|of|up|but|on|yet|by|or)\b/i,
first_word = /^(\W*)(\w*)/,
last_word = /(\w*)(\W*)$/;
function capitalize(word) {
return word.slice(0, 1).toUpperCase() + word.slice(1).toLowerCase();
}
function capitalize_mid(word) {
return lower_case.exec(word) ? word.toLowerCase() : capitalize(word);
}
return function to_title_case(str) {
var prefix = first_word.exec(str),
str_minus_prefix = str.slice(prefix[0].length),
suffix = last_word.exec(str_minus_prefix),
center = str_minus_prefix.slice(0, -suffix[0].length);
return prefix[1] + capitalize(prefix[2]) + center.replace(/\w+/g, capitalize_mid)
+ capitalize(suffix[1]) + suffix[2];
};
})();
$(document).ready(function() {
$(".onclick").click(function () {
var text = $(this).parents('.ui-grid-a').find('.ui-block-a').text();
var html = '<div data-role="main"class="ui-content">'
+ '<div class="ui-grid-b"><div class="ui-block-a">'
+ to_title_case(text) + '</div><div class="ui-block-b">More Info</div>'
+ '<div class="ui-block-c">Unfavorite</div></div></div>';
$("#favorites").append(html);
});
});
I using PhoneGap to create a Geolocation App following this excellent tutorial (link). Unfortunatelly, I'm having an issue that I can't figure out. The relevant parts that are giving me a headache are these:
//Section 1
$('#history').on('pageshow', function () {
tracks_recorded = window.localStorage.length;
$("#tracks_recorded").html("<strong>" + tracks_recorded + "</strong> workout(s) recorded");
$("#history_tracklist").empty();
for (i = 0; i < tracks_recorded; i++) {
$("#history_tracklist").append("<li><a href='#track_info' data-ajax='false'>" + window.localStorage.key(i) + "</a></li>");
}
$("#history_tracklist").listview('refresh');
});
//Section 2
$("#history_tracklist li a").on('click', function () {
$("#track_info").attr("track_id", $(this).text());
});
//Section 3
$('#track_info').on('pageshow', function () {
var key = $(this).attr("track_id");
$("#track_info div[data-role=header] h1").text(key);
var data = window.localStorage.getItem(key);
data = JSON.parse(data);
});
Section 1 works just fine, the data is stored, and the list is created without any issues. But then in Section 2 is when everything goes to hell. By clicking on the element, a new attribute (track_id) is supposed to be created, but it doesn't. Therefore, in Section 3, the "var key" won't get a value, and as a consequence, "var data" will be null also. As you can imagine, nothing works from there. What am I doing wrong here? I only included what I considered the relevant code, but if more is needed I'll do so. Thansk!
In section 2, I think you just need to delegate click handling to the "#history_tracklist" container, as follows :
$("#history_tracklist").on('click', "li a", function () {
$("#track_info").attr("track_id", $(this).text());
});
Without delegation you have a rule saying :
when any existing li a element within #history_tracklist is clicked execute my function
With delegation, you have a rule saying :
when any existing or future li a element within #history_tracklist is clicked execute my function
Adi Palaz's Nested Accordion
This seems like it should be simple but I can't seem to figure this out and I have been sitting here slamming my head on my desk after like four hours without a solution.
You will notice in the demo on the page that there are expand all/collapse all buttons that fire a function to open all the accordion panels or close them.
I DON'T want to use those buttons. I want to write my own function and fire the expand all or collapse all function after the user completes a gesture on a DIV somewhere else on the page.
But I can't seem to figure out how to call the same function the author is using on the buttons to properly expand and collapse the accordion panels.
If it helps, I set up a test page to play with:
http://dl.dropbox.com/u/22224/Newfolder/nested_accordion_demo3.html
And here are the two scripts it needs to work:
Nested Accordion Script
Expand.js
Please help! I am desperate and the author is not responding to emails!
I was able to solve the expand/collapse all problem with the following code, hope it will work for you as well.
function expand(id) {
var o = $.extend({}, $.fn.accordion.defaults, null);
var containerID = o.container ? id : '', objID = o.objID ? o.objID : o.obj + o.objClass, Obj = o.container ? containerID + ' ' + objID : id, El = Obj + ' ' + o.el, hTimeout = null;
$(El + ' a.trigger').closest(o.wrapper).find('> ' + o.next).show().closest(o.wrapper).find('a.trigger').addClass('open').data('state', 1);
}
function collapse(id) {
var o = $.extend({}, $.fn.accordion.defaults, null);
var containerID = o.container ? id : '', objID = o.objID ? o.objID : o.obj + o.objClass, Obj = o.container ? containerID + ' ' + objID : id, El = Obj + ' ' + o.el, hTimeout = null;
$(El + ' a.trigger').closest(o.wrapper).find('> ' + o.next).not('.shown').hide().closest(o.wrapper).find('a.open').removeClass('open').data('state', 0);
}
Example:
expand('#accordion1');
collapse('#accordion1');
I'm faced with the exact same problem and would like to know if you ever found an answer?
The work around I plan to use is something like $('.accordion a').click(); to programmatically click each link in the list - not pretty but it seems to work...
Two years later and I have the same desire... use Adi Palaz nested accordion, but have my OWN styled and specific action expand/collapse all buttons. I did finally get it to work the way I wanted even though I am admittedly a novice level jquery programmer. Here's my key learnings:
I started with example nested_accordion_demo5.html with the #acc1
example. I did not use expand.js at all, I never could get it to
work.
I changed the function defaults in my demo5.html to add the obj: "div".
$("#acc1").accordion({
obj: "div",
el: ".h",
head: "h4, h5",
next: "div",
iconTrigger: true
});
Then I added a div with class="accordion" around the whole ul structure and removed the accordion class from the ul.
I made my own two expand/collapse links and put them inside the div but before the ul. Later I got fancy and added styling to make them look like buttons, but first I got it working.
[Expand All] [Collapse All]
Then I added two new separate event handlers to jquery.nestedAccordion.js using snippets from the one that handles the a.trigger events. I placed them immediately after the existing event handler. Here's my new CollapseAll:
$(Obj).delegate('a.CollapseAll', ev, function(ev) {
$( o.obj + o.objClass ).find('a.trigger').each(function() {
var $thislink = $(this), $thisWrapper = $thislink.closest(o.wrapper), $nextEl = $thisWrapper.find('> ' + o.next);
if (($nextEl).length && $thislink.data('state') && (o.collapsible)) {
$thislink.removeClass('open');
$nextEl.filter(':visible')[o.hideMethod](o.hideSpeed, function() {$thislink.data('state', 0);});
}
});
});
Then I made a second event handler that does the ExpandAll.
I know this could likely be much more efficient, but I am thrilled to at least have it working given my skill level!
UPDATE: A commenter told me to change some codes, this is the new code and its not working neither.
I'm creating a Facebook-Like chat. It gets the latest messages "Not Read" from a JSON file and it appends the text to an "UL" element vía "LI" into a box. If the box doesn't exist, it creates and attach the text. I want that when I click that div, it hides using margin-bottom negative, and when I click it again it shows by Margin-Bottom:0. Please help me since it's just not working.
function showChat(id){
$(this).animate({marginBottom : "0"}).removeClass("hidden_box").addClass("active_box").removeAttr('onclick').click(function(){
hideChat(Id);
});
}
function hideChat(id){
$(this).animate({marginBottom : "-270px"}).removeClass("active_box").addClass("hidden_box").click(function(){
showChat(Id);
});
}
function getOnJSON(){
//Creating Variables that will be used
var from;var to;var msg_id;var msg_txt;
//Getting the data from the json file
$.getJSON("/ajax/chat.json.php",function(data){
//Repeat for each result
$.each(data.notif, function(i,data){
//Getting a var to info
from = data.from;to = data.to;msg_id = data.id;msg_txt = data.text;
//check if div exists
if ($("#chat_"+from+"_lp").length === 0){
//If not, create the div
$("#boxes").append('<div id="chat_'+from+'_lp" class="chat_box hidden_box clickable_box"></div>');
//Add the senders name
$("#chat_"+from+"_lp").append('<div id="'chat_+from+'_nick" class="chat_name">'+from+'</div>');
//Add the chats UL
$("#chat_"+from+"_lp").append('<ul id="chat_'+from+'_txt" class="chat_txt"></ul>');
//Add the message text
$("#chat_"+from+"_lp").append('<li id="' + msg_id + '">'+ msg_txt+'</li>');
//Add event handler for each div
$('#chat_'+from+'_lp').click(function() {showChat(this);});
//If div exists just add the text
}else{
//Add the message text
$("#chat_"+from+"_txt").append('<li id="' + msg_id + '">'+ msg_txt+'</li>');
//Add event handler for each document
$('#chat_'+from+'_lp').click(function() {showChat(this);});
//Close If
}
//Close data for each item
});
//Close JSON
});
//Close Function
}
UPDATE 2: in order to stop making and appending things, I made an unique HTML string that is going to be appended.
new_chat_string = '<div id="chat_'+from+'_lp" class="chat_box hidden_box clickable_box"><div id="'chat_+from+'_nick" class="chat_name">'+from+'</div><ul id="chat_'+from+'_txt" class="chat_txt"><li id="' + msg_id + '">'+ msg_txt+'</li></ul></div>';
$("#boxes").append(new_chat_string);
use class instead of id
<div id="chat_sender_lp" class="chat_box hidden_box clickable_box sender-click"
then
$('.hidden_box.sender-click').live('click', function(){
$(this).slideToggle(500);
});
After:
$("#boxes").append('<div id="chat_'+from+'_lp" class="chat_box hidden_box clickable_box" ><div id="name">'+from+'</div><ul id="chat_'+from+'_txt" class="chat_txt"><li id="' + msg_id + '">'+ msg_txt+'</li></ul></div>');
Add the event handler for the inserted element:
$('#chat_'+from+'_lp').click(function() { showChat(this) })
"this" passes a DOM reference to itself.
Keep in mind that you're adding: <div id="name"> every time. IDs must be unique. Use a class name instead.
EDIT:
Appending to the DOM is really quite slow. It's actually more efficient to build up your HTML as a string and just insert it in one go. Also, you only really need to stick and ID on the wrapping element. Everything else can be derived from that using a jQuery selector. It helps you write much cleaner code.
Here's the string you need to append:
'<div id="chat_'+msg_id+'" class="chat_box hidden_box clickable_box">
<div class="chat_name">'+from+'</div><ul class="chat_txt"><li>
'+msg_txt+'</li></ul></div>'
If you wanted to select chat name later, you'd use: $('chat_1 .chat_name').html()
It also makes more semantic sense to hook up your click handler to an A tag. So you'd use:
$('#chat_'+msg_id).find('a').click(function() {showChat(this);});
The code is a lot cleaner and easier to follow this way. I hope this helps.
I've written the following code to add a clickable "link button" to a section of my page.
var linkButtonHtml = "<a data-makeId='" + makeId + "' href='javascript:expandMake(" + makeId + "," + categoryId + ")'>+</a> " + makeName;
var divHtml = "<div style='display:none' class='models' data-makeId='" + makeId + "'></div>" + "<br/>";
html += linkButtonHtml + divHtml;
$('#linkDiv').html(html);
The code works fine, but it's ugly and difficult to read with all the string concatenation.
As you can see, I am building anchor elements and div elements with string concatenation. The target of my anchor element is a javascript function invocation with two arguments. Is there a good jQuery way to improve the readability of this code?
I'm not sure if this really improves readability is here is a 100% jQuery solutions:
$(html)
.append(
$('<a />')
.attr('data-makeId', makeId)
.attr('href', 'javascript:void(0);')
.click(function(event)
{
// Prevent clicking the link from leaving the page.
event.preventDefault();
expandMake(makeId, categoryId);
})
.text('+'))
.append(
document.createTextNode(makeName)
)
.append(
$('<div />')
.addClass('models')
.attr('data-makeId=', makeId)
.hide());
Where "html" in $(html) is the html variable you have in your sample.
jQuery offers an option for a second argument when creating elements.
var linkButton = $('<a>',{'data-makeId':makeId,
href:'#',
click:function(){expandMake( makeId, categoryId )},
text:'+'
});
var div = $('<div>',{ style:'display:none',
'class':'models',
'data-makeId': makeId
})
.after('<br>');
$('#linkDiv')
.empty()
.append(html)
.append(linkButton)
.append( makeName )
.append(div);
EDIT: Fixed an issue where makeName was not appended.
Only real way is either abstracting some of your tag generation or spread the script out a little to make it more readable : http://jsfiddle.net/3dYPX/1/
Your also using jQuery so you might want to consider changing the way you trigger the javascript. Try looking into the .live() event. (Ill just get an example up, not that its very important)
Using live event for unobtrusive javascript:
http://jsfiddle.net/3dYPX/2/
It is all being done inside of the onLoad event at the moment, just to use as an example.
Use a template library such as
jQuery Templates instead of inlining
HTML.
Instead of using "javascript:" URLs, attach event handlers to the generated DOM fragments.
Refrain from using inline styles.
Something like:
$('#linkDiv')
.empty()
.append($.tmpl(myTemplate, {
makeId: makeId,
makeName: makeName,
categoryId: categoryId
}))
.click(function () {
var makeId = $(this).attr("data-makeId");
if (makeId) {
expandMake(makeId, $(this).attr("data-categoryId"));
}
});
Where myTemplate has the content:
${makeName}
<div class="models" data-makeId="${makeId}"></div>
Instead of using an inline style to initially hide the models, hide them all with a general CSS rule, and then selectively show them with a class:
.models { display: none }
.models.shown { display: block }
Just add the "shown" class to show a certain block of models.
Here you go:
$('#linkDiv').empty().append([
$('+').data('makeId', makeId).click(function(e) {
e.preventDefault();
expandMake(makeId, categoryId);
})[0],
$('<span>').text(makeName)[0],
$('<div class="models">').data('makeId', makeId).hide()[0],
$('<br>')[0]
]);
Live demo: http://jsfiddle.net/cncbm/1/
Consider this:
$('#linkDiv').data({'makeId': makeId, 'categoryId': categoryId}).empty().append([
$('+').click(expandMake)[0],
$('<span>').text(makeName)[0],
$('<div class="models">').hide()[0]
]);
So, you define that data-stuff on the parent DIV (the common parent) and you re-factor the expandMake function so that it reads those data-values from the parent DIV instead of passing them as arguments.