<div class="title">
I am text node
<a class="edit">Edit</a>
</div>
我希望获取“我是文本节点”,不想删除“编辑”标记,并且需要一个跨浏览器的解决方案。<div class="title">
I am text node
<a class="edit">Edit</a>
</div>
我希望获取“我是文本节点”,不想删除“编辑”标记,并且需要一个跨浏览器的解决方案。var text = $(".title").contents().filter(function() {
return this.nodeType == Node.TEXT_NODE;
}).text();
这将获取所选元素的内容
,并对其应用一个筛选函数。筛选函数仅返回文本节点(即nodeType == Node.TEXT_NODE
的节点)。
text()
,因为 filter
函数返回的是节点本身,而不是节点的内容。 - James AllardicejQuery("*").each(function() {
console.log(this.nodeType);
})
并且我得到了所有节点类型的 1。 - Batandwa您可以使用以下方法获取第一个childNode的nodeValue:
$('.title')[0].childNodes[0].nodeValue
null
作为返回值。 - Anthony Rutledge另一个可以对“复杂”或深度嵌套元素有用的本地JS解决方案是使用NodeIterator。将NodeFilter.SHOW_TEXT
作为第二个参数(“whatToShow”),并迭代该元素的仅文本节点子节点。
var root = document.querySelector('p'),
iter = document.createNodeIterator(root, NodeFilter.SHOW_TEXT),
textnode;
// print all text nodes
while (textnode = iter.nextNode()) {
console.log(textnode.textContent)
}
<p>
<br>some text<br>123
</p>
您还可以使用TreeWalker
。两者之间的区别在于,NodeIterator
是一个简单的线性迭代器,而TreeWalker
则允许您通过兄弟和祖先进行导航。
返回第一个 #text 节点的内容的 ES6 版本
const extract = (node) => {
const text = [...node.childNodes].find(child => child.nodeType === Node.TEXT_NODE);
return text && text.textContent.trim();
}
.from()
创建一个浅复制的数组实例。(2) 使用 .find()
进行使用 .nodeName
进行字符串比较时更好的选择是使用 node.NodeType === Node.TEXT_NODE
。(3) 如果没有值,返回空字符串,null
,如果没有找到文本节点,则更为准确。如果没有找到文本节点,则可能需要创建一个!如果返回空字符串""
,就会给人一种虚假印象,认为存在文本节点可以正常操作。从本质上讲,返回空字符串是一种善意的谎言,最好避免。 - Anthony Rutledge[...node.childNodes]
将 HTMLCollection 转换为数组。 - vsyncchild.NodeType
应该是 child.nodeType
(小写的 nodeType)。 - Vaviloffvar oDiv = document.getElementById("MyDiv");
var firstText = "";
for (var i = 0; i < oDiv.childNodes.length; i++) {
var curNode = oDiv.childNodes[i];
if (curNode.nodeName === "#text") {
firstText = curNode.nodeValue;
break;
}
}
你可以在这里看到它的运行效果:http://jsfiddle.net/ZkjZJ/
curNode.nodeType == 3
而不是 nodeName
。 - NilloccurNode.nodeType == Node.TEXT_NODE
(数字比较速度更快,但是curNode.nodeType== 3不易读——哪个节点具有数字3?) - mikepcurNode.NodeType === Node.TEXT_NODE
。这个比较是在一个未知可能迭代的循环中发生的。比较两个小数比比较不同长度的字符串更好(时间和空间考虑)。在这种情况下正确的问题是“我有什么类型的节点?”,而不是“我叫什么名字?” https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType - Anthony RutledgechildNodes
,请注意一个元素节点可能有多个文本节点。在通用解决方案中,您可能需要指定要针对元素节点内的哪个文本节点实例进行目标定位(第一个、第二个、第三个等等...)。 - Anthony Rutledge在查找 DOM 中的文本时,始终牢记此事项。
这个问题会让您注意您的 XML / HTML 结构。
在这个纯 JavaScript 示例中,我考虑到可能存在与其他节点交错的多个文本节点。然而,最初我对空格不作判断,留下过滤任务给其他代码处理。
在这个版本中,我从调用 / 客户端代码中传递一个 NodeList
。
/**
* Gets strings from text nodes. Minimalist. Non-robust. Pre-test loop version.
* Generic, cross platform solution. No string filtering or conditioning.
*
* @author Anthony Rutledge
* @param nodeList The child nodes of a Node, as in node.childNodes.
* @param target A positive whole number >= 1
* @return String The text you targeted.
*/
function getText(nodeList, target)
{
var trueTarget = target - 1,
length = nodeList.length; // Because you may have many child nodes.
for (var i = 0; i < length; i++) {
if ((nodeList[i].nodeType === Node.TEXT_NODE) && (i === trueTarget)) {
return nodeList[i].nodeValue; // Done! No need to keep going.
}
}
return null;
}
当然,通过首先测试 node.hasChildNodes()
,就没有必要使用预测试的for
循环。
/**
* Gets strings from text nodes. Minimalist. Non-robust. Post-test loop version.
* Generic, cross platform solution. No string filtering or conditioning.
*
* @author Anthony Rutledge
* @param nodeList The child nodes of a Node, as in node.childNodes.
* @param target A positive whole number >= 1
* @return String The text you targeted.
*/
function getText(nodeList, target)
{
var trueTarget = target - 1,
length = nodeList.length,
i = 0;
do {
if ((nodeList[i].nodeType === Node.TEXT_NODE) && (i === trueTarget)) {
return nodeList[i].nodeValue; // Done! No need to keep going.
}
i++;
} while (i < length);
return null;
}
这里的getTextById()
函数使用了两个辅助函数:getStringsFromChildren()
和filterWhitespaceLines()
。
getStringsFromChildren()
/**
* Collects strings from child text nodes.
* Generic, cross platform solution. No string filtering or conditioning.
*
* @author Anthony Rutledge
* @version 7.0
* @param parentNode An instance of the Node interface, such as an Element. object.
* @return Array of strings, or null.
* @throws TypeError if the parentNode is not a Node object.
*/
function getStringsFromChildren(parentNode)
{
var strings = [],
nodeList,
length,
i = 0;
if (!parentNode instanceof Node) {
throw new TypeError("The parentNode parameter expects an instance of a Node.");
}
if (!parentNode.hasChildNodes()) {
return null; // We are done. Node may resemble <element></element>
}
nodeList = parentNode.childNodes;
length = nodeList.length;
do {
if ((nodeList[i].nodeType === Node.TEXT_NODE)) {
strings.push(nodeList[i].nodeValue);
}
i++;
} while (i < length);
if (strings.length > 0) {
return strings;
}
return null;
}
filterWhitespaceLines()
/**
* Filters an array of strings to remove whitespace lines.
* Generic, cross platform solution.
*
* @author Anthony Rutledge
* @version 6.0
* @param textArray a String associated with the id attribute of an Element.
* @return Array of strings that are not lines of whitespace, or null.
* @throws TypeError if the textArray param is not of type Array.
*/
function filterWhitespaceLines(textArray)
{
var filteredArray = [],
whitespaceLine = /(?:^\s+$)/; // Non-capturing Regular Expression.
if (!textArray instanceof Array) {
throw new TypeError("The textArray parameter expects an instance of a Array.");
}
for (var i = 0; i < textArray.length; i++) {
if (!whitespaceLine.test(textArray[i])) { // If it is not a line of whitespace.
filteredArray.push(textArray[i].trim()); // Trimming here is fine.
}
}
if (filteredArray.length > 0) {
return filteredArray ; // Leave selecting and joining strings for a specific implementation.
}
return null; // No text to return.
}
getTextById()
/**
* Gets strings from text nodes. Robust.
* Generic, cross platform solution.
*
* @author Anthony Rutledge
* @version 6.0
* @param id A String associated with the id property of an Element.
* @return Array of strings, or null.
* @throws TypeError if the id param is not of type String.
* @throws TypeError if the id param cannot be used to find a node by id.
*/
function getTextById(id)
{
var textArray = null; // The hopeful output.
var idDatatype = typeof id; // Only used in an TypeError message.
var node; // The parent node being examined.
try {
if (idDatatype !== "string") {
throw new TypeError("The id argument must be of type String! Got " + idDatatype);
}
node = document.getElementById(id);
if (node === null) {
throw new TypeError("No element found with the id: " + id);
}
textArray = getStringsFromChildren(node);
if (textArray === null) {
return null; // No text nodes found. Example: <element></element>
}
textArray = filterWhitespaceLines(textArray);
if (textArray.length > 0) {
return textArray; // Leave selecting and joining strings for a specific implementation.
}
} catch (e) {
console.log(e.message);
}
return null; // No text to return.
}
接下来,返回值(数组或null)将发送到客户端代码,并应该进行处理。希望该数组应该具有实际文本的字符串元素,而不是空格行。
不会返回空字符串(""),因为需要文本节点来正确指示有效文本的存在。返回("")可能会给出误导,让人们认为存在文本节点,从而假设他们可以通过更改.nodeValue的值来更改文本。但在空字符串的情况下不存在文本节点,这是错误的。
示例1:
<p id="bio"></p> <!-- There is no text node here. Return null. -->
例子2:
<p id="bio">
</p> <!-- There are at least two text nodes ("\n"), here. -->
当您希望通过添加空格来使HTML易于阅读时,问题就出现了。尽管没有可读的有效文本,但仍然存在包含换行符("\n"
)的文本节点在它们的.nodeValue
属性中。
对于人类来说,示例一和示例二是功能等效的-空元素正在等待填充。DOM与人类推理不同。这就是为什么getStringsFromChildren()
函数必须确定是否存在文本节点,并将.nodeValue
值收集到数组中的原因。
for (var i = 0; i < length; i++) {
if (nodeList[i].nodeType === Node.TEXT_NODE) {
textNodes.push(nodeList[i].nodeValue);
}
}
在示例二中,存在两个文本节点,getStringFromChildren()
将返回它们的.nodeValue
("\n"
)。然而,filterWhitespaceLines()
使用正则表达式过滤出纯空格字符组成的行。null
而不是换行符("\n"
)对客户端/调用代码来说是否属于欺骗?从人类的角度来看,不是。从DOM角度来看,是的。但是,这里的问题是获取文本,而不是编辑它。没有人类可读的文本可以返回给调用代码。<p></p>
元素中编辑文本并且有额外的空白(例如2),可能意味着破坏(或跳过)段落标记之间除了一个文本节点以外的所有内容,以确保元素包含应该显示的内容。.nodeValue
属性具有你想要编辑的真实、可读的文本的方法。filterWhitespaceLines
可以帮助我们完成其中一半。var whitespaceLine = /(?:^\s+$)/; // Non-capturing Regular Expression.
for (var i = 0; i < filteredTextArray.length; i++) {
if (!whitespaceLine.test(textArray[i])) { // If it is not a line of whitespace.
filteredTextArray.push(textArray[i].trim()); // Trimming here is fine.
}
}
此时,您可能已经看到了类似于以下内容的输出:
["Dealing with text nodes is fun.", "Some people just use jQuery."]
不能保证这两个字符串在DOM中相邻,因此使用.join()
合并它们可能会产生一个不自然的组合。相反,在调用getTextById()
的代码中,您需要选择要使用的字符串。
测试输出。
try {
var strings = getTextById("bio");
if (strings === null) {
// Do something.
} else if (strings.length === 1) {
// Do something with strings[0]
} else { // Could be another else if
// Do something. It all depends on the context.
}
} catch (e) {
console.log(e.message);
}
.trim()
可添加在getStringsFromChildren()
内,以消除前导和尾随空格(或将一堆空格转换为零长度字符串("")),但如何事先知道每个应用程序找到文本(字符串)后需要发生什么?你不知道,所以让具体实现来处理,让getStringsFromChildren()
保持通用。$('.title').clone() //clone the element
.children() //select all the children
.remove() //remove all the children
.end() //again go back to selected element
.text(); //get the text of element
a
元素内的文本:http://jsfiddle.net/ekHJH/ - James Allardice.
,这意味着你实际上获取的是 title
元素的文本,而不是具有 class="title"
的元素。 - James Allardice只使用原生JavaScript:
const el = document.querySelector('.title');
const text = el.firstChild.textContent.trim();
var oDiv = document.getElementById("MyDiv");
var firstText = "";
for (var i = 0; i < oDiv.childNodes.length; i++) {
var curNode = oDiv.childNodes[i];
whitespace = /^\s*$/;
if (curNode.nodeName === "#text" && !(whitespace.test(curNode.nodeValue))) {
firstText = curNode.nodeValue;
break;
}
}
在 jsfiddle 上查看:- http://jsfiddle.net/webx/ZhLep/
curNode.nodeType === Node.TEXT_NODE
会更好。在循环中使用字符串比较和正则表达式是低效的解决方案,特别是当 oDiv.childNodes.length
的数量级增加时。这个算法解决了 OP 的具体问题,但是可能会付出可怕的性能代价。如果文本节点的排列或数量发生变化,则无法保证此解决方案返回准确的输出。换句话说,您无法定位到所需的确切文本节点。您要看 HTML 结构和文本排列的情况。 - Anthony RutledgeArray.from(document.querySelector("#title").childNodes).find(n => n.nodeType == Node.TEXT_NODE).textContent