jQuery Selector Confusion - javascript

I have the following code..
<span class="under">
texthere
<ul class="list">
<li> list text here</li>
</ul>
</span>
When i run $(".under").text() I get "textherelist text here" .
I've tried $(".under :not(.list)").text() and get underfined.
I also dont get the correct output for $(".under").not(".list").text()
So my last attemp was $(".list").parent().text()
which results in textherelist text here
Where am i going wrong with something so simple?
p.s. doesn't have to be jQuery can be JavaScript if its simpler.
Wanted result: texthere

So I'm guessing you're after the text : texthere ?
var elem = $(".under").clone(),
text = $.trim(elem.children().remove().end().text());
FIDDLE
Clone the element, remove all children elements and get the remaining text.
​

From the docs:
Description: Get the combined text contents of each element in the set
of matched elements, including their descendants.
So yes, that behavior is expected.
You can try this to get only the immediate text node of a selector:
$('.under').contents().filter(function(){ return(this.nodeType == 3); }).text()
Explanation:
.contents() (docs) returns the children of a selector, including textnodes
Description: Get the children of each element in the set of matched
elements, including text and comment nodes.
.filter() takes a callback to return only things you need, based on this, you are only taking those with nodeType == 3, which is a text node.
http://jsfiddle.net/R4Pzf/

Here you go:
var text = $('.under').contents(':not(.list)').text();
Example:
http://jsfiddle.net/ak4FU/1/

Related

How to replace only text content on an element?

I have the following structure:
<li>
<a><i>...</i>Some Text Here!</a>
<input>
<a></a>
</li>
I want just to change the string Some Text Here! under the element li without affecting (removing or changing) anything else, that is all its child nodes.
Apparently, textContent, if set, removes all descendant elements. Other similar methods seem to do the same thing. I have searched for a while, but no solution seems to solve this.
I can set classes or ids to whatever element you see in the structure, if that helps.
You can also get the element contents and check each nodeType to be a TEXT_NODE
Jquery version:
$('a').contents().filter(function () {
return this.nodeType === Node.TEXT_NODE;
})
.each(function () {
this.nodeValue = 'Something else';
});
Javascript version:
var nodes = document.getElementsByTagName('a')[0].childNodes;
for (var i in nodes){
if (nodes[i].nodeType === Node.TEXT_NODE)
nodes[i].nodeValue = 'Something else';
}
Supposing you already have the <a> tag in a variable anchor:
anchor.lastChild.textContent = 'My text';
Look at the example below:
var anchor = document.querySelector('a');
anchor.lastChild.textContent = 'Other text';
<li>
<a><i>Lalala...</i>Some Text Here!</a>
<input>
<a></a>
</li>
Use Element.childNodes which covers text nodes along with elements and replace their textContent:
document.getElementsByTagName('a')[0].childNodes[1].textContent='Something else';
It obviously depends on how many tags there are, whether there are spaces between them, which text you want to replace, etc.
A demo:
document.getElementsByTagName('p')[0].childNodes[1].textContent = ' Something else.';
<p><i>I’m an element.</i> Some Text Here!</p>
You can't. Corrected.
However, for cleanliness sake you should wrap the text in a span and then replace the contents of the span. It's much easier to read, like so:
HTML
<li>
<a><i>...</i><span class="replace-me">Some text here!</span></a>
</li>
jQuery
$('.replace-me').text('Some new text here now!');

How to use jQuery selector/find() against text string without inserting into the DOM?

I have a text string i'm trying to select the spans from using jQuery. I'd like to grab the spans w/o adding the element to the dom -- if that's possible?
After reading the jquery docs i was under the assumption that i could create a fragment by wrapping the string in a jquery selector tag, then using.find() to find the elements i want.
I have code that is similar to this but from the looks of the last line, it's obvious that no spans are being selected; any help would be greatly appreciated:
// 'text' is normally generated automatically...
// just made it an escaped string for example purposes.
var text ="<span id=\"blah1\">Y</span><br/><span id=\"blah2\">o</span><br/>";
var spans = $(text).find('span');
console.log(text); // => <span id="blah1">Y</span><br/><span id="blah2">o</span><br/>
console.log(spans.length); // => 0
Thanks.
You want to use filter(), not find()
var text ="<span id=\"blah1\">Y</span><br/><span id=\"blah2\">o</span><br/>";
var spans = $(text).filter('span');
console.log(spans.length);
jsFiddle
From the jQuery docs
filter:
The supplied selector is tested against each element; all elements
matching the selector will be included in the result.
find:
the .find() method allows us to search through the descendants of
these elements in the DOM tree and construct a new jQuery object from
the matching elements.
with your html fragment, there is no wrapper element, so there is no descendants, hence why find() does not work.
You are basically doing:
var elems = jQuery("<span id=\"blah1\">Y</span>").add("<br/>").add("<span id=\"blah2\">o</span>").add("<br/>");
If you want find to work with find(), you need to wrap it in an element.
var text ="<span id=\"blah1\">Y</span><br/><span id=\"blah2\">o</span><br/>";
var spans = jQuery("<div></div>").append(text).find("span");
console.log(spans.length);
You want to use filter in this case:
var text ="<span id=\"blah1\">Y</span><br/><span id=\"blah2\">o</span><br/>";
var spans = $(text).filter('span');
console.log(spans.length); // 2
Demo: http://jsfiddle.net/ambiguous/TGY3J/
Or wrap it in a <div> and use find:
var text ="<span id=\"blah1\">Y</span><br/><span id=\"blah2\">o</span><br/>";
var spans = $('<div>' + text + '</div>').find('span');
console.log(spans.length); // 2
Demo: http://jsfiddle.net/ambiguous/qbCjk/
find works on descendants but without the <div> wrapper, your $(text) doesn't have any <span> descendants. Wrapping your HTML in a <div> is probably your best bet, that way you don't have to worry about how deep your desired elements are.

How do I use jQuery to get a set of every element with certain text, but NOT the parents?

I want to use jQuery to select every element that has a certain text string in it, but not the parents of that element. How do I do this? I have no control whatsoever over the HTML code, but here is an example:
<body>
<div>
<p>This is a paragraph and <a>this is not</a>.</p>
Here we have a div.
</div>
</body>
If I use the word "this" as my match word, I want jQuery to provide me with a set containing the <a> and the <p>, but not the <div> or the <body>.
Again, I have no control AT ALL over the HTML!
Thanks!
** Clarification: I do want the parent of the element if the parent ALSO has a "this" in its immediate text. Thus, I want the <a> AND the <p>.
Update::
Here is what I came up with: jsfiddle
var myArray = $('*:contains("this")','body').filter(function(){
if($(this).contents().filter(function(){
return(this.nodeType == 3);
}).text().indexOf('this')===-1){
return false;
}
return true;
});
$.each(myArray,function(){
console.log(this.nodeName);
});
Starts similar to the link posted by Robin, but it forces to only search in the context of body elements - this keeps your scripts safe if they are not inline.
The next part is a filter that checks to see if the current element direct text nodes contain the text.
This is a bit convoluted, but to walk through it:
.contents() - docs - gets the immediate nodes
.filter() - docs - we want to only test on test nodes, so we filter them out
this.nodeType - w3 spec - check to see if its a text node
.test() - docs - gets a string of the text nodes.
.indexOf() - check that string for our string
Note I did the :contains() at the top and in the second filter, the first isn't needed per say but I think the initial test should reduce the number of deeper tests and speed it up slightly.
Here's my solution with pure JS.
Code:
function findElmsWithWord(word, elm, found){
if (elm.nodeType === 3 && elm.data.indexOf(word) !== -1)
found.push(elm.parentNode);
else
for (var i = 0; i < elm.childNodes.length; i++)
findElmsWithWord(word, elm.childNodes[i], found);
}
var elms = [];
findElmsWithWord('this', document.body, elms);
console.log(elms);
It recursively walks the dom until it finds the text nodes that contain the word in question. And then adds the text node's parent as a result.

get the text in a list item and modify it with javascript

I have a bunch of HTML list items and what i want is to be able to read the text of a list item and then be able to give the user the ability to modify it. the structure of each list item is the same:
<li id='1' class='attachment'>
<a href='link.html'>TEXT VALUE I WANT TO READ AND MODIFY</a>
<a href='link2.html'><img src'img1.jpg'></a>
<a href='link3.html'><img src'img2.jpg'></a>
</li>
at the moment i am able to get the list item using
li = document.getElementById(id);
but if i get it to print out something like
li.childNodes[?]
li.childNodes[?].data
li.childNodes[?].nodeValue etc...
i am never able to get it to print "TEXT VALUE I WANT TO READ AND MODIFY", it always gives null, undefined or [object Text] or something similar. any help would be greatly appreciated.
You need to get the textContent or innerText properties:
var li = document.getElementById(id);
var t = "textContent" in li.childNodes[0] ? "textContent" : "innerText";
alert(li.childNodes[0][t]);
innerText is implemented by IE and textContent is implemented by standards compliant browsers.
Alternatively, if you're certain that the element's first child will be a text node, you can use
alert(li.childNodes[0].childNodes[0].nodeValue);
Based on your comment below, we can make a safe assumption that white-space is interfering with the childNodes property in your example. This is normal - childNodes returns both elements and text nodes that are direct descendants of the given element. The alternative is to use children in newer browsers:
// 1st example using `children` instead
var li = document.getElementById(id);
var t = "textContent" in li ? "textContent" : "innerText";
alert(li.children[0][t]);
// 2nd example using `children` instead
alert(li.children[0].childNodes[0].nodeValue);
children isn't supported pre Firefox 3.5 and in other older browsers. You could use li.getElementsByTagName("a") for the example that you've posted - but bear in mind that it will return all <a> element descendants, not just immediate children.
What about this:
var li = document.getElementById(1);
var a = li.getElementsByTagName('a');
alert(a[0].innerHTML);
And before anyone can suggest to just use jQuery, here's the equivalent:
$('li#someid a').html();
In my opinion, if you're doing fine without jQuery, don't use it.

How do I remove descendant elements from a jQuery wrapped set?

I'm a little confused about which jQuery method and/or selectors to use when trying to select an element, and then remove certain descendant elements from the wrapped set.
For example, given the following HTML:
<div id="article">
<div id="inset">
<ul>
<li>This is bullet point #1.</li>
<li>This is bullet point #2.</li>
<li>This is bullet point #3.</li>
</ul>
</div>
<p>This is the first paragraph of the article</p>
<p>This is the second paragraph of the article</p>
<p>This is the third paragraph of the article</p>
</div>
I want to select the article:
var $article = $('#article');
but then remove <div id="inset"></div> and its descendants from the wrapped set. I tried the following:
var $article = $('#article').not('#inset');
but that didn't work, and in retrospect, I think I can see why. I also tried using remove() unsuccessfully.
What would be the correct way to do this?
Ultimately, I need to set this up in such a way that I can define a configuration array, such as:
var selectors = [
{
select: '#article',
exclude: ['#inset']
}
];
where select defines a single element that contains text content, and exclude is an optional array that defines one or more selectors to disregard text content from.
Given the final wrapped set with the excluded elements removed, I would like to be able to call jQuery's text() method to end up with the following text:
This is the first paragraph of the article.This is the second paragraph of the article.This is the third paragraph of the article.
The configuration array doesn't need to work exactly like that, but it should provide roughly equivalent configuration potential.
Thanks for any help you can provide!
I suppose you do not want to modify the original HTML by removing elements from it, but you want to just get the content of article without the inset.
Thats why I would use clone() to get a copy of the article and then remove the inset from it.
Like this:
$("#article").clone().find("#inset").remove().end().text()
$("#article") selects the article div, clone creates a
copy,
find gets the children to
remove (you could also use children),
remove(), removes the selected inset,
end() goes back to the original selection.
At the end I just added text() as you mentioned you wanted to do that.
if you want to remove anything in #article but #inset use:
$('#article > *:not(#inset)').remove() // selects all direct children of #article but not #inset and removes them
see an example here: http://jsfiddle.net/zwPsD/
if want to apply this rule to more then one DOM element you can chain them:
$('#article, #article2, #article3, #etc').find('> *').not('#inset, #that, #and. #there').remove()
you can find an example of this here:
http://jsfiddle.net/ZNjdE/
and with a simple each you can extract the text:
http://jsfiddle.net/ZNjdE/2/
Unless I am missing something, why can't you select all of the <p> elements within the article div?
$("#article p")
If that is unacceptable, I think you are looking for the filter function...
$("#article").filter(":not(#inset)")
Note: you can have multiple selectors within the :not() selector. They just have to be comma delimited, so this approach should accomodate your configurational needs.
Try something like this.
$('#article').children(':not(#inset)').each(function(){
alert($(this).text());
});
If you want to do it with an object:
var selectors = {
select: '#article',
exclude: ['#inset', 'p']
};
$(selectors.select).children(':not('+selectors.exclude.join(',')+')').each(function(){
alert($(this).text());
});
EDIT
To get any level of ancestor, you could add extra selectors and use find(). Eg.
$('#article').find('li:first, :not(#inset, #inset *)').each(function(){
alert($(this).text());
});
With this you'd be excluding #inset and all #inset's ancestors except the first li. It won't quite work with the selectors object from before though because you're excluding a group of elements and then including some of the excluded ones. You could do it with three elements in the object:
var selectors = {select: ... , exclude: ... , includeFromExcluded: ...};

Categories