<div class="title">
I am text node
<a class="edit">Edit</a>
</div>
ฉันต้องการรับ "I am text node" ไม่ต้องการลบแท็ก "แก้ไข" และต้องการโซลูชันข้ามเบราว์เซอร์
<div class="title">
I am text node
<a class="edit">Edit</a>
</div>
ฉันต้องการรับ "I am text node" ไม่ต้องการลบแท็ก "แก้ไข" และต้องการโซลูชันข้ามเบราว์เซอร์
คำตอบ:
var text = $(".title").contents().filter(function() {
return this.nodeType == Node.TEXT_NODE;
}).text();
สิ่งนี้จะได้รับcontents
ขององค์ประกอบที่เลือกและใช้ฟังก์ชันตัวกรองกับองค์ประกอบนั้น ฟังก์ชันตัวกรองจะส่งคืนเฉพาะโหนดข้อความ (เช่นโหนดที่มีnodeType == Node.TEXT_NODE
)
text()
เนื่องจากfilter
ฟังก์ชันส่งคืนโหนดเองไม่ใช่เนื้อหาของโหนด
jQuery("*").each(function() { console.log(this.nodeType); })
และฉันได้1สำหรับโหนดทุกประเภท
คุณสามารถรับ nodeValue ของ childNode แรกโดยใช้
$('.title')[0].childNodes[0].nodeValue
null
ค่าตอบแทน
หากคุณหมายถึงรับค่าของโหนดข้อความแรกในองค์ประกอบรหัสนี้จะทำงาน:
var 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
เช่นกัน
curNode.nodeType == Node.TEXT_NODE
(การเปรียบเทียบตัวเลขเร็วกว่า แต่ curNode.nodeType == 3 ไม่สามารถอ่านได้ - โหนดใดมีหมายเลข 3)
curNode.NodeType === Node.TEXT_NODE
ใช้ การเปรียบเทียบนี้เกิดขึ้นภายในวงรอบของการทำซ้ำที่ไม่รู้จัก การเปรียบเทียบตัวเลขขนาดเล็กสองตัวจะดีกว่าการเปรียบเทียบสตริงที่มีความยาวต่างกัน (การพิจารณาเวลาและพื้นที่) คำถามที่ถูกต้องที่จะถามในสถานการณ์นี้คือ "ฉันมีโหนดแบบใด / ประเภทใด" ไม่ใช่ "ฉันมีชื่ออะไร" developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
childNodes
โปรดทราบว่าโหนดองค์ประกอบสามารถมีโหนดข้อความได้มากกว่าหนึ่งโหนด ในโซลูชันทั่วไปเราอาจต้องระบุอินสแตนซ์ของโหนดข้อความภายในโหนดองค์ประกอบที่คุณต้องการกำหนดเป้าหมาย (ตัวแรกวินาทีที่สาม ฯลฯ ... )
อีกวิธี 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
ให้คุณนำทางผ่านพี่น้องและบรรพบุรุษได้เช่นกัน
ก่อนอื่นโปรดคำนึงถึงสิ่งนี้เสมอเมื่อค้นหาข้อความใน 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.
}
จากนั้นค่าส่งคืน (Array หรือ 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
แทนอักขระ newline ( "\n"
) เป็นรูปแบบของการโกหกไคลเอนต์ / รหัสการโทรหรือไม่ ในแง่ของมนุษย์ไม่ ในแง่ DOM ใช่ อย่างไรก็ตามปัญหาที่นี่คือการรับข้อความไม่ได้แก้ไข ไม่มีข้อความของมนุษย์ที่จะกลับไปยังรหัสการโทร
ไม่มีทางรู้ได้ว่ามีอักขระขึ้นบรรทัดใหม่กี่ตัวใน HTML ของใครบางคน การสร้างตัวนับที่มองหาอักขระขึ้นบรรทัดใหม่ "วินาที" นั้นไม่น่าเชื่อถือ มันอาจไม่มีอยู่จริง
แน่นอนยิ่งไปกว่านั้นปัญหาของการแก้ไขข้อความใน<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()
เป็นแบบทั่วไป
อาจมีบางครั้งที่target
ไม่จำเป็นต้องมีความจำเพาะระดับนี้ ( และเช่นนั้น) เป็นสิ่งที่ดี. ใช้วิธีง่ายๆในกรณีเหล่านั้น อย่างไรก็ตามอัลกอริทึมทั่วไปช่วยให้คุณสามารถรองรับสถานการณ์ที่เรียบง่ายและซับซ้อนได้
ES6 เวอร์ชันที่ส่งคืนเนื้อหาโหนด #text แรก
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
จะเป็นจริงมากขึ้นหากไม่พบโหนดข้อความ หากไม่พบโหนดข้อความอาจต้องสร้างขึ้นมาใหม่! หากคุณส่งคืนสตริงว่าง""
คุณอาจให้การแสดงผลที่ผิดพลาดว่ามีโหนดข้อความอยู่และสามารถจัดการได้ตามปกติ โดยพื้นฐานแล้วการส่งคืนสตริงว่างเปล่าเป็นเรื่องโกหกสีขาวและหลีกเลี่ยงได้ดีที่สุด
[...node.childNodes]
ในการแปลงHTMLCollectionเป็น Arrays
$('.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
องค์ประกอบด้วย: jsfiddle.net/ekHJH
.
จุดเริ่มต้นของตัวเลือกซึ่งหมายความว่าคุณได้รับข้อความของtitle
องค์ประกอบไม่ใช่องค์ประกอบด้วยclass="title"
.innerText
เป็นแบบแผนเก่าของ IE ที่เพิ่งนำมาใช้ ในแง่ของการเขียนสคริปต์ DOM มาตรฐานnode.nodeValue
เป็นวิธีที่หนึ่งในการจับข้อความของโหนดข้อความ
สิ่งนี้จะไม่สนใจช่องว่างเช่นกันดังนั้นคุณไม่เคยมีรหัส Blank textNodes..code โดยใช้ Javascript หลัก
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 และการจัดเรียงข้อความในนั้น
คุณยังสามารถใช้การtext()
ทดสอบโหนดของ XPath เพื่อรับโหนดข้อความเท่านั้น ตัวอย่างเช่น
var target = document.querySelector('div.title');
var iter = document.evaluate('text()', target, null, XPathResult.ORDERED_NODE_ITERATOR_TYPE);
var node;
var want = '';
while (node = iter.iterateNext()) {
want += node.data;
}
นี่คือวิธีการแก้ปัญหาของฉันใน ES6 เพื่อสร้างสตริง contraining ข้อความที่ตัดแบ่งของ childNodes ทั้งหมด (recursive) โปรดทราบว่ายังไปที่ shdowroot ของ childnodes
function text_from(node) {
const extract = (node) => [...node.childNodes].reduce(
(acc, childnode) => [
...acc,
childnode.nodeType === Node.TEXT_NODE ? childnode.textContent.trim() : '',
...extract(childnode),
...(childnode.shadowRoot ? extract(childnode.shadowRoot) : [])],
[]);
return extract(node).filter(text => text.length).join('\n');
}
การแก้ปัญหานี้ได้รับแรงบันดาลใจจากการแก้ปัญหาของhttps://stackoverflow.com/a/41051238./1300775