I have added an .on() function to my dynamically added list elements with jQuery. The problem I'm facing is that the button that triggers the click lives inside the dynamically added li elements and therefore
$(this).parent().text()
returns the text from the li element as well as the button's text.
See the following fiddle: http://jsfiddle.net/TL5TR/
How can I get around this problem and only display the li text (without the button text)? Would I have to rework my code in a way that button will be placed outside the li tag?
Update
Before I accept one of these answers -- all of them which are working by the way, so thank you, can you also explain to me what are the pros/cons of using one method or the other - i.e. textNode vs span tag.
I think the easiest solution would be to add span around your text:
$('#list').append("<li><span>" + item + "</span><button class='clickme'>Click me</button></li>");
And then:
$(this).siblings("span").text()
If the textNode is the previous sibling of the clicked button you can use previousSibling property.
$("#list").on('click', 'button', function() {
alert(this.previousSibling.nodeValue);
});
http://jsfiddle.net/cZk8H/
var array = ["First", "Second", "Third"];
$.each(array, function(index, item) {
$('#list').append("<li><span>" + item + "</span> <button class='clickme'>Click me</button></li>");
});
$("#list").on('click', 'button', function() {
alert($(this).parent().find("span").text());
});
put the text in a span tag
http://jsfiddle.net/TL5TR/2/
Try Adding text wrapped in a span. And you should easily address the sibling's value, later:
var array = ["First", "Second", "Third"];
$.each(array, function(index, item) {
$('#list').append("<li><span>" + item + "</span><button class='clickme'>Click me</button></li>");
});
$("#list").on('click', 'button', function() {
alert($(this).siblings().text());
});
http://jsfiddle.net/TL5TR/1/
PROS (of using span) : Better Management of text inside the li. You actually refer to the content enclosed in the span always....
PROS(of not using span) : Lesser tags in the HTML that you need to take care of. Although in this case I would say it's more of a micro management to think about it.
Or instead of separating it out, you could just do this:
var array = ["First", "Second", "Third"];
$.each(array, function(index, item) {
$("<li>" + item + " <button class='clickme'>Click me</button></li>")
.appendTo('#list')
.on('click', function() { alert(item); });
});
Unless of course you are adding these some other way. Then I would use one of the other suggested answers.
You can clone the element and remove the children elements:
$(this).parent().clone().children().remove().end().text()
Related
I am fairly new to JS, and have created a little piece of script and it does exactly what I want which is find some elements then adds elements with data populated from via ajax....
So I go from this...
<select><select/>
to this...
<select>
<option value="{ajax value data}"> {ajax text data} <option/>
...
<select/>
using this piece of script...
filteredSelectIds.forEach(function (item) {
let itemId = '#' + item;
let itemData = item.split('-')[0] + 's';
$.each(data[itemData], function (i, color) {
$(itemId).append($('<option/>', {
value: color.optionValue,
text : color.optionText
}));
});
});
Now, what I am trying to do is at the same time add a Font Awesome icon to each element so I need to end up with something like this,,,,
<select>
<option value="{ajax value data}"><i class="fa fa-icon"> {ajax text data} <i/><option/>
...
<select/>
How would I do that??
I'm also new at JS, try this.
element = '<i class="fa fa-icon"> {0} <i/>'.format("{ajax text data}")
$('<option/>').append( element );
So #brk gave me this solution which worked, and would work for putting an Element inside another
"Create the option tag & i tag & first append itag to option tag and then append option tag to item"
filteredSelectIds.forEach(function (item) {
let itemId = '#' + item;
let itemData = item.split('-')[0] + 's';
$.each(data[itemData], function (i, color) {
var selOption = $('<option value="' + color.optionValue + '"></option>');
selOption.append('<i class="fa fa-icon">'+color.optionText+'<i/>');
$(itemId).append(selOption); }); });
However, although this placed the element inside the element as I wanted, and this could principle could probably be used to place any element within another, Tibrogargan correctly pointed to a question that makes the point that elements cannot be place within elements (Not really the Point of my question, but helpful). My solution was simply using the unicode for the Font Awesome icon and escaping it with \u then used \xa0 for additional spaces as follows:-
filteredSelectIds.forEach(function (item) {
let itemId = '#' + item;
let itemData = item.split('-')[0] + 's';
$.each(data[itemData], function (i, color) {
$(itemId).append($('<option/>', {
value: color.optionValue,
text : '\ue905 \xa0\xa0\xa0' +color.optionText
}));
});
});
Thanks!
I have a bunch of divs with matching ids (#idA_1 and #idB_1, #idA_2 and #idB_2, etc). In jquery I wanted to assign click functions, so that when I click an #idA it will show and hide an #idB.
Basically I want to make this:
$(".idA_x").click(function(){
$("idB_x").toggleClass("hide")
});
X would be a variable to make #idA and #idB match. I could write each individually, but that would take too much code, is there a way to make the number in the id into a variable?
Sure, you can do:
var num = 13;
addButtonListener(num);
function addButtonListener(num){
$("#idA_"+num).click(function(){
$("#idB_"+num).toggleClass("hide")
});
}
Try JQuery solution :
var x = 1;
$(".idA_" + x ).click(function(){
$(".idB_" + x ).toggleClass("hide")
});
Hope this helps.
There are many ways to achieve that, but what you probably want is to create a shared CSS class, e.g. .ids, and bind the event listener to that one:
$('.ids').click(function () {
//...
});
Then you can handle your logic in a cleaner way within the function body.
In order to make it dynamic, and not have to repeat the code for each one of your numbers, I suggest doing as follows:
First, add a class to all the div's you want to be clickable .clickable, and then use the id of the clicked event, replacing A with B in order to select the element you what to toggle the class:
$(".clickable").click(function(){
var id = $(this).attr('id');
$("#" + id.replace('A', 'B')).toggleClass("hide");
});
Or, you can also select all divs and use the contains wildcard:
$("div[id*='idA_']").click(function(){
var id = $(this).attr('id');
$("#" + id.replace('A', 'B')).toggleClass("hide");
});
This solution won't have the need to add a class to all clickable divs.
You can use attribute selector begins with to target the id's you want that have corresponding elements.
https://api.jquery.com/attribute-starts-with-selector/
Then get the value after the understore using split on the id and applying Array.pop() to remove the 1st part of the array.
http://jsfiddle.net/up9h0903/
$("[id^='idA_']").click(function () {
var num = this.id.split("_").pop();
$("#idB_" + num).toggleClass("hide")
});
Using regex would be your other option to strip the number from the id.
http://jsfiddle.net/up9h0903/1/
$("[id^='idA_']").click(function () {
var num = this.id.match(/\d+/g);
$("#idB_" + num).toggleClass("hide")
});
I have the following HTML snippet:
<span class="target">Change me <a class="changeme" href="#">now</a></span>
I'd like to change the text node (i.e. "Change me ") inside the span from jQuery, while leaving the nested <a> tag with all attributes etc. intact. My initial huch was to use .text(...) on the span node, but as it turns out this will replace the whole inner part with the passed textual content.
I solved this with first cloning the <a> tag, then setting the new text content of <span> (which will remove the original <a> tag), and finally appending the cloned <a> tag to my <span>. This works, but feels such an overkill for a simple task like this. Btw. I can't guarantee that there will be an initial text node inside the span - it might be empty, just like:
<span class="target"><a class="changeme" href="#">now</a></span>
I did a jsfiddle too. So, what would be the neat way to do this?
Try something like:
$('a.changeme').on('click', function() {
$(this).closest('.target').contents().not(this).eq(0).replaceWith('Do it again ');
});
demo: http://jsfiddle.net/eEMGz/
ref: http://api.jquery.com/contents/
Update:
I guess I read your question wrong, and you're trying to replace the text if it's already there and inject it otherwise. For this, try:
$('a.changeme').on('click', function() {
var
$tmp = $(this).closest('.target').contents().not(this).eq(0),
dia = document.createTextNode('Do it again ');
$tmp.length > 0 ? $tmp.replaceWith(dia) : $(dia).insertBefore(this);
});
Demo: http://jsfiddle.net/eEMGz/3/
You can use .contents():
//set the new text to replace the old text
var newText = 'New Text';
//bind `click` event handler to the `.changeme` elements
$('.changeme').on('click', function () {
//iterate over the nodes in this `<span>` element
$.each($(this).parent().contents(), function () {
//if the type of this node is undefined then it's a text node and we want to replace it
if (typeof this.tagName == 'undefined') {
//to replace the node we can use `.replaceWith()`
$(this).replaceWith(newText);
}
});
});
Here is a demo: http://jsfiddle.net/jasper/PURHA/1/
Some docs for ya:
.contents(): http://api.jquery.com/contents
.replaceWith(): http://api.jquery.com/replacewith
typeof: https://developer.mozilla.org/en/JavaScript/Reference/Operators/typeof
Update
var newText = 'New Text';
$('a').on('click', function () {
$.each($(this).parent().contents(), function () {
if (typeof this.tagName == 'undefined') {
//instead of replacing this node with the replacement string, just replace it with a blank string
$(this).replaceWith('');
}
});
//then add the replacement string to the `<span>` element regardless of it's initial state
$(this).parent().prepend(newText);
});
Demo: http://jsfiddle.net/jasper/PURHA/2/
You can try this.
var $textNode, $parent;
$('.changeme').on('click', function(){
$parent = $(this).parent();
$textNode= $parent.contents().filter(function() {
return this.nodeType == 3;
});
if($textNode.length){
$textNode.replaceWith('Content changed')
}
else{
$parent.prepend('New content');
}
});
Working demo - http://jsfiddle.net/ShankarSangoli/yx5Ju/8/
You step out of jQuery because it doesn't help you to deal with text nodes. The following will remove the first child of every <span> element with class "target" if and only if it exists and is a text node.
Demo: http://jsfiddle.net/yx5Ju/11/
Code:
$('span.target').each(function() {
var firstChild = this.firstChild;
if (firstChild && firstChild.nodeType == 3) {
firstChild.data = "Do it again";
}
});
This is not a perfect example I guess, but you could use contents function.
console.log($("span.target").contents()[0].data);
You could wrap the text into a span ... but ...
try this.
http://jsfiddle.net/Y8tMk/
$(function(){
var txt = '';
$('.target').contents().each(function(){
if(this.nodeType==3){
this.textContent = 'done ';
}
});
});
You can change the native (non-jquery) data property of the object. Updated jsfiddle here: http://jsfiddle.net/elgreg/yx5Ju/2/
Something like:
$('a.changeme3').click(function(){
$('span.target3').contents().get(0).data = 'Do it again';
});
The contents() gets the innards and the get(0) gets us back to the original element and the .data is now a reference to the native js textnode. (I haven't tested this cross browser.)
This jsfiddle and answer are really just an expanded explanation of the answer to this question:
Change text-nodes text
$('a.changeme').click(function() {
var firstNode= $(this).parent().contents()[0];
if( firstNode.nodeType==3){
firstNode.nodeValue='New text';
}
})
EDIT: not sure what layout rules you need, update to test only first node, otherwise adapt as needed
If you click on a cell on this page, it loads the larger version of the image. I'm trying to achieve this same effect.
What I have gotten so far: http://jsfiddle.net/8mYW9/
First off I know having the "appear" <div> is redundant - is there a good way to utilize $(this) and appendTo(); instead?
Ultimately my idea is to grab the id of the anchor contained within the div that is clicked and to append it to the cell. What should I be doing...?
If you change the ID attribute to class for the appear elements you can do this:
$(document).ready(function() {
$('#appear').hide();
$('.links').click(function() {
var $this = $(this);//cache the $(this) selector since it will be used more than once
$this.children('.appear').html('item id: ' + $this.children('a').attr('id')).fadeToggle('slow');
});
});
Demo: http://jsfiddle.net/8mYW9/7/
BTW you can't have multiple elements with the same ID in a HTML document.
You could do that with:
$(document).ready(function() {
$('#appear').hide();
$('.links').click(function() {
$(this).append('<div>' + $(this).find('a:first').attr('id') + '</div>');
});
});
JS Fiddle demo.
Amended so that only one id is shown (others are removed before showing the latest):
$(document).ready(function() {
$('#appear').hide();
$('.links').click(function() {
$(this).closest('.container').find('.appended').remove();
$(this).append('<div class="appended">' + $(this).find('a:first').attr('id') + '</div>');
});
});
JS Fiddle demo.
Incidentally, it escaped my notice the first time, but with multiple elements sharing the same id you have invalid (X)HTML: an id must be unique within the document (citation: W3.org).
References:
attr().
closest().
find().
:first selector.
remove().
Try using class selectors instead. You've got duplicate IDs:
$(document).ready(function() {
$('.appear').hide();
$('.links').click(function() {
$(this).find(".appear").fadeToggle('slow', function() {
$(this).html('item id:')
});
});
});
http://jsfiddle.net/8mYW9/
What's the best way of appending an array of jQuery elements to the page?
I know that if I'm appending straight HTML tags then they should be collected together in an array then the array appended. This is the fastest method. However I have a collection of jQuery elements that have events created on them so I can't just concatenate strings together.
Example:
function displayCommentActions(actions) {
var html = [];
module.$element.find('.comments .actions li').remove();
$.each(actions, function(key, value) {
html.push($('<li class="' + key + '">' + value.name + '</li>').bind({
click: function(event) {
alert('found click');
}
}));
});
$.each(html, function(count, item) {
module.$element.find('.comments .actions').append(item);
})
};
This is ugly because it takes two loops. One to create the jQuery objects and one to output them to the page. How can I improve my code?
#efritz, doing .remove() on every list item is slow and expensive, a quick wipe of list items would be .empty().
#Samuel you can't avoid looping here. .append() takes either a string (of html) or a jquery object. It can't take an array of jquery objects.
var actionsList = module.$element.find('.comments .actions');
actionsList.empty() // Remove all contents of our <UL>
$.each(actions, function(class, value) {
var listItem = $('<li />').addClass(class) // Create our <li> and add our class
.append('' + value.name + '') // Add our link
.click(function() { // Click bind event
alert('Clicked item');
});
actionsList.append(listItem); // Add our <li> to our <ul>
}
This is probably as small as you're gonna get it Samuel, it's an improvement from your dual-loop at least.
Would something like this not work?
function displayCommentActions(actions) {
var target = module.$element.find('.comments .actions li')
target.remove();
$.each(actions, function(k, v) {
var item = $('<li />').addClass(key);
var link = $('<a />')
.attr('href', 'javascript:void(0);')
.html(value.name)
.click(function() {
alert('found click');
});
elem.append(item);
target.append(item);
}
}
Also, remember that repeated selectors can be expensive - store them if at all possible.