If I have html like this:
This is some text
First span text
I am not a jquery expert, but how about,
$('#listItem').children().first().text()
isn't the code:
var text = $('#listItem').clone().children().remove().end().text();
just becoming jQuery for jQuery's sake? When simple operations involve that many chained commands & that much (unnecessary) processing, perhaps it is time to write a jQuery extension:
(function ($) {
function elementText(el, separator) {
var textContents = [];
for(var chld = el.firstChild; chld; chld = chld.nextSibling) {
if (chld.nodeType == 3) {
textContents.push(chld.nodeValue);
}
}
return textContents.join(separator);
}
$.fn.textNotChild = function(elementSeparator, nodeSeparator) {
if (arguments.length<2){nodeSeparator="";}
if (arguments.length<1){elementSeparator="";}
return $.map(this, function(el){
return elementText(el,nodeSeparator);
}).join(elementSeparator);
}
} (jQuery));
to call:
var text = $('#listItem').textNotChild();
the arguments are in case a different scenario is encountered, such as
<li>some text<a>more text</a>again more</li>
<li>second text<a>more text</a>again more</li>
var text = $("li").textNotChild(".....","<break>");
text will have value:
some text<break>again more.....second text<break>again more
I presume this would be a fine solution also - if you want to get contents of all text nodes that are direct children of selected element.
$(selector).contents().filter(function(){ return this.nodeType == 3; }).text();
Note: jQuery documentation uses similar code to explain contents function: https://api.jquery.com/contents/
P.S. There's also a bit uglier way to do that, but this shows more in depth how things work, and allows for custom separator between text nodes (maybe you want a line break there)
$(selector).contents().filter(function(){ return this.nodeType == 3; }).map(function() { return this.nodeValue; }).toArray().join("");
To be able to trim the result, use DotNetWala's like so:
$("#foo")
.clone() //clone the element
.children() //select all the children
.remove() //remove all the children
.end() //again go back to selected element
.text()
.trim();
I found out that using the shorter version like document.getElementById("listItem").childNodes[0]
won't work with jQuery's trim().
I propose to use the createTreeWalker to find all texts elements not attached to html elements (this function can be used to extend jQuery):
function textNodesOnlyUnder(el) {
var resultSet = [];
var n = null;
var treeWalker = document.createTreeWalker(el, NodeFilter.SHOW_TEXT, function (node) {
if (node.parentNode.id == el.id && node.textContent.trim().length != 0) {
return NodeFilter.FILTER_ACCEPT;
}
return NodeFilter.FILTER_SKIP;
}, false);
while (n = treeWalker.nextNode()) {
resultSet.push(n);
}
return resultSet;
}
window.onload = function() {
var ele = document.getElementById('listItem');
var textNodesOnly = textNodesOnlyUnder(ele);
var resultingText = textNodesOnly.map(function(val, index, arr) {
return 'Text element N. ' + index + ' --> ' + val.textContent.trim();
}).join('\n');
document.getElementById('txtArea').value = resultingText;
}
<li id="listItem">
This is some text
<span id="firstSpan">First span text</span>
<span id="secondSpan">Second span text</span>
</li>
<textarea id="txtArea" style="width: 400px;height: 200px;"></textarea>
This is a good way for me
var text = $('#listItem').clone().children().remove().end().text();