Если у меня есть html:
<li id="listItem">
This is some text
<span id="firstSpan">First span text</span>
<span id="secondSpan">Second span text</span>
</li>
Я пытаюсь использовать .text()
для извлечения только строки "Это какой-то текст", но если бы я сказал $('#list-item').text()
, я получаю "Это текст textFirst span textSecond span".
Есть ли способ получить (и, возможно, удалить через нечто вроде .text("")
) только свободный текст внутри тега, а не текст в его дочерних тегах?
HTML не был написан мной, поэтому я должен работать. Я знаю, что было бы просто просто обернуть текст в тегах при написании html, но опять же, html предварительно написан.
Мне понравилась эта многократно используемая реализация, основанная на методе clone()
найденном здесь, чтобы получить только текст внутри родительского элемента.
Код предоставлен для удобства пользования:
$("#foo")
.clone() //clone the element
.children() //select all the children
.remove() //remove all the children
.end() //again go back to selected element
.text();
Простой ответ:
$("#listItem").contents().filter(function(){
return this.nodeType == 3;
})[0].nodeValue = "The text you want to replace with"
Это похоже на случай чрезмерного использования jquery для меня. Следующее возьмет текст, игнорируя другие узлы:
document.getElementById("listItem").childNodes[0];
Вам нужно обрезать это, но вы получите то, что хотите, в одной простой линии.
ИЗМЕНИТЬ
Вышеприведенный текст получит node. Чтобы получить фактический текст, используйте это:
document.getElementById("listItem").childNodes[0].nodeValue;
$('.foo')[0].childNodes[0].nodeValue.trim()
Проще и быстрее:
$("#listItem").contents().get(0).nodeValue
Подобно принятому ответу, но без клонирования:
$("#foo").contents().not($("#foo").children()).text();
И для этого используется плагин jQuery:
$.fn.immediateText = function() {
return this.contents().not(this.children()).text();
};
Вот как использовать этот плагин:
$("#foo").immediateText(); // get the text without children
не является кодом:
var text = $('#listItem').clone().children().remove().end().text();
просто становится jQuery для jQuery саке? Когда простые операции включают в себя множество цепочечных команд и такую (ненужную) обработку, возможно, пришло время написать расширение jQuery:
(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));
для вызова:
var text = $('#listItem').textNotChild();
аргументы в случае возникновения другого сценария, например
<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>");
текст будет иметь значение:
some text<break>again more.....second text<break>again more
Попробуйте следующее:
$('#listItem').not($('#listItem').children()).text()
Это должно быть нечто, соответствующее потребностям, которые зависят от структуры, которую вы представили. Для примера, который вы предоставили, это работает:
$(document).ready(function(){
var $tmp = $('#listItem').children().remove();
$('#listItem').text('').append($tmp);
});
Демо: http://jquery.nodnod.net/cases/2385/run
Но это довольно зависит от того, что разметка похожа на то, что вы разместили.
clone
если это не намеченный эффект.
.detach()
вместо .remove()
.
$($('#listItem').contents()[0]).text()
Короткий вариант Ответ Стюарта.
или с get()
$($('#listItem').contents().get(0)).text()
jQuery.fn.ownText = function () {
return $(this).contents().filter(function () {
return this.nodeType === Node.TEXT_NODE;
}).text();
};
Я предполагаю, что это тоже прекрасное решение - если вы хотите получить содержимое всех текстовых узлов, которые являются прямыми дочерними элементами выбранного элемента.
$(selector).contents().filter(function(){ return this.nodeType == 3; }).text();
Примечание. Документация jQuery использует аналогичный код для объяснения функции содержимого: https://api.jquery.com/contents/
P.S. Там также немного уродливый способ сделать это, но это показывает более подробно, как все работает, и позволяет настраивать разделитель между текстовыми узлами (возможно, вы хотите, чтобы там была разбита строка)
$(selector).contents().filter(function(){ return this.nodeType == 3; }).map(function() { return this.nodeValue; }).toArray().join("");
Это старый вопрос, но главный ответ очень неэффективен. Здесь лучшее решение:
$.fn.myText = function() {
var str = '';
this.contents().each(function() {
if (this.nodeType == 3) {
str += this.textContent || this.innerText || '';
}
});
return str;
};
И просто сделайте это:
$("#foo").myText();
Если index
позиции текстового узла фиксирован среди его братьев и сестер, вы можете использовать
$('parentselector').contents().eq(index).text()
Точно так же, как вопрос, я пытался извлечь текст, чтобы сделать некоторую подстановку текста в регулярном выражении, но у меня были проблемы, когда мои внутренние элементы (то есть: <i>
, <div>
, <span>
и т.д.) Также получали удален.
Следующий код, кажется, работает хорошо и решил все мои проблемы.
Он использует некоторые ответы, представленные здесь, но, в частности, будет заменять текст только тогда, когда элемент имеет nodeType === 3
.
$(el).contents().each(function() {
console.log(" > Content: %s [%s]", this, (this.nodeType === 3));
if (this.nodeType === 3) {
var text = this.textContent;
console.log(" > Old : '%s'", text);
regex = new RegExp("\\[\\[" + rule + "\\.val\\]\\]", "g");
text = text.replace(regex, value);
regex = new RegExp("\\[\\[" + rule + "\\.act\\]\\]", "g");
text = text.replace(regex, actual);
console.log(" > New : '%s'", text);
this.textContent = text;
}
});
То, что сделано выше, это циклическое прохождение всех элементов данного el
(которое было просто получено с помощью $("div.my-class[name='some-name']");
Для каждого внутреннего элемента оно в основном игнорирует их Для каждой части текста (как определено в if (this.nodeType === 3)
) будет применена замена регулярного выражения только к этим элементам.
Часть this.textContent = text
просто заменяет замещенный текст, который в моем случае я искал токены типа [[min.val]]
, [[max.val]]
и т.д.
Эта выдержка из короткого кода поможет любому, кто пытается сделать то, о чем спрашивал вопрос... и немного больше.
Я предлагаю использовать createTreeWalker, чтобы найти все элементы текста, не прикрепленные к элементам html (эта функция может использоваться для расширения 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>
Я придумал конкретное решение, которое должно быть намного более эффективным, чем клонирование и модификация клона. Это решение работает только со следующими двумя оговорками, но должно быть более эффективным, чем принятое в настоящее время решение:
С учетом сказанного здесь приведен код:
// 'element' is a jQuery element
function getText(element) {
var text = element.text();
var childLength = element.children().text().length;
return text.slice(0, text.length - childLength);
}
просто поместите его в <p>
или <font>
и возьмите этот $('# listItem font'). text()
Первое, что пришло в голову
<li id="listItem">
<font>This is some text</font>
<span id="firstSpan">First span text</span>
<span id="secondSpan">Second span text</span>
</li>
Вы можете попробовать это
alert(document.getElementById('listItem').firstChild.data)
Чтобы иметь возможность обрезать результат, используйте DotNetWala следующим образом:
$("#foo")
.clone() //clone the element
.children() //select all the children
.remove() //remove all the children
.end() //again go back to selected element
.text()
.trim();
Я узнал, что использование более короткой версии, такой как document.getElementById("listItem").childNodes[0]
, не будет работать с jQuery trim().
document.getElementById("listItem").childNodes[0]
представляет собой простой javascript, вам нужно будет обернуть его в функцию jQuery $(document.getElementById("listItem").childNodes[0]).trim()
Используйте дополнительное условие для проверки того, являются ли innerHTML и innerText одинаковыми. Только в этих случаях замените текст.
$(function() {
$('body *').each(function () {
console.log($(this).html());
console.log($(this).text());
if($(this).text() === "Search" && $(this).html()===$(this).text()) {
$(this).html("Find");
}
})
})
Это хороший способ для меня
var text = $('#listItem').clone().children().remove().end().text();
Я не эксперт jquery, но как насчет
$('#listItem').children().first().text()
Это не проверено, но я думаю, что вы можете попробовать что-то вроде этого:
$('#listItem').not('span').text();
$('#listItem').text()
. #listItem
не является <span>
поэтому добавление not('span')
ничего не делает.