Bir elemanın metin düğümü nasıl alınır?


101
<div class="title">
   I am text node
   <a class="edit">Edit</a>
</div>

"Metin düğümüyüm" almak istiyorum, "düzenle" etiketini kaldırmak istemiyorum ve tarayıcılar arası bir çözüme ihtiyacım var.


bu soru stackoverflow.com/questions/3172166/… ile hemen hemen aynıdır - James'in yanıtının sade bir JS sürümü için bu yanıtlara bakın
Mala

Yanıtlar:


80
var text = $(".title").contents().filter(function() {
  return this.nodeType == Node.TEXT_NODE;
}).text();

Bu contents, seçilen öğenin bilgisini alır ve ona bir filtre işlevi uygular. Filtre işlevi yalnızca metin düğümlerini (yani olan düğümleri nodeType == Node.TEXT_NODE) döndürür .


@Val - üzgünüm, bunu orijinal koddan kaçırdım. Yanıtı göstermek için güncelleyeceğim. İhtiyacınız var, text()çünkü filterişlev düğümlerin içeriğini değil, düğümlerin kendisini döndürür.
James Allardice

1
Neden olduğundan emin değilim ama yukarıdaki teoriyi test ederken başarısız oluyorum. Aşağıdakini çalıştırdım ve tüm düğüm türleri için 1jQuery("*").each(function() { console.log(this.nodeType); }) aldım .
Batandwa

Tıklanan düğümde metin ve tüm alt öğelerinde metin almak mümkün mü?
Jenna Kwon

Bu ilginç ve bu sorunu çözüyor, ancak durum daha karmaşık hale geldiğinde ne oluyor? İşi halletmenin daha esnek bir yolu var.
Anthony Rutledge

JQuery olmadan document.querySelector (". Title"). ChildNodes [0] .nodeValue
Balaji Gunasekaran


16

Öğedeki ilk metin düğümünün değerini almak istiyorsanız, bu kod çalışacaktır:

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;
    }
}

Bunu eylem halinde burada görebilirsiniz: http://jsfiddle.net/ZkjZJ/


Onun curNode.nodeType == 3yerine kullanabileceğini düşünüyorum nodeName.
Nilloc

1
@Nilloc muhtemelen, ama kazancı ne?
Gölge Sihirbazı Senin İçin Kulak

5
@ShadowWizard @Nilloc bunun için sabitleri kullanmak için önerilen bir yol ... curNode.nodeType == Node.TEXT_NODE(sayısal karşılaştırma daha hızlıdır ancak curNode.nodeType == 3 okunamaz - hangi düğümün numarası 3?)
mikep

1
@ShadowWizard Kullanımı curNode.NodeType === Node.TEXT_NODE. Bu karşılaştırma, bilinmeyen olası yinelemelerden oluşan bir döngü içinde gerçekleşir. İki küçük sayıyı karşılaştırmak, çeşitli uzunluklardaki dizeleri karşılaştırmaktan daha iyidir (zaman ve mekan konuları). Bu durumda sorulacak doğru soru, "ne tür / tür düğüme sahibim?" Sorusudur ve "hangi adım var?" Değil. developer.mozilla.org/en-US/docs/Web/API/Node/nodeType
Anthony Rutledge,

2
@ShadowWizard Ayrıca, elemek için bir döngü kullanacaksanız childNodes, bir eleman düğümünün birden fazla metin düğümüne sahip olabileceğini bilin . Genel bir çözümde, hedeflemek istediğiniz bir öğe düğümü içindeki bir metin düğümünün hangi örneğini (birinci, ikinci, üçüncü vb.) Belirtmeniz gerekebilir.
Anthony Rutledge,

14

"Karmaşık" veya derinlemesine iç içe geçmiş öğeler için yararlı olabilecek başka bir yerel JS çözümü, NodeIterator'ı kullanmaktır . NodeFilter.SHOW_TEXTİkinci bağımsız değişken olarak ("whatToShow") koyun ve öğenin yalnızca metin düğümü çocuklarını yineleyin.

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>

Ayrıca kullanabilirsiniz TreeWalker. İkisi arasındaki fark NodeIterator, TreeWalkerkardeşler ve atalar arasında gezinmenize izin verirken , basit bir doğrusal yineleyici olmasıdır .


9

Saf JavaScript: Minimalist

Öncelikle, DOM'da metin ararken bunu daima aklınızda bulundurun.

MDN - DOM'daki beyaz boşluk

Bu sorun, XML / HTML'nizin yapısına dikkat etmenizi sağlayacaktır.

Bu saf JavaScript örneğinde, diğer türden düğümlerle araya sokulabilecek birden çok metin düğümünün olasılığını hesaba katıyorum . Bununla birlikte, başlangıçta, bu filtreleme görevini başka bir koda bırakarak beyaz boşluklar hakkında bir yargıda bulunmam.

Bu versiyonda, NodeListarayan / müşteri kodundan bir giriş yapıyorum .

/**
* 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;
}

Elbette, önce test ederek node.hasChildNodes(), bir ön test fordöngüsü kullanmaya gerek kalmayacaktır .

/**
* 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;
}

Saf JavaScript: Sağlam

Burada işlev getTextById()iki yardımcı işlev kullanır: getStringsFromChildren()ve 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.
}

Daha sonra, dönüş değeri (Array veya null), işlenmesi gereken istemci koduna gönderilir. Umarım, dizide beyaz boşluk satırları değil, gerçek metin dize öğeleri olmalıdır.

Boş dizeleri ( "") olan değil düzgün geçerli metnin varlığını göstermek için bir metin düğümü gerektiğinden döndü. Returning ( ""), bir metin düğümünün var olduğu yönünde yanlış bir izlenim verebilir ve bir kişinin değerini değiştirerek metni değiştirebileceğini varsayabilir .nodeValue. Bu yanlıştır, çünkü boş bir dizge olması durumunda bir metin düğümü mevcut değildir.

Örnek 1 :

<p id="bio"></p> <!-- There is no text node here. Return null. -->

Örnek 2 :

<p id="bio">

</p> <!-- There are at least two text nodes ("\n"), here. -->

HTML'nizi aralıklarla okumayı kolaylaştırmak istediğinizde sorun ortaya çıkar. Şimdi, okunabilir hiçbir geçerli metin olmamasına rağmen, özelliklerinde hala satırsonu ( "\n") karakterleri olan metin düğümleri vardır.nodeValue .

İnsanlar, birinci ve ikinci örnekleri işlevsel olarak eşdeğer olarak görürler - doldurulmayı bekleyen boş öğeler. DOM, insan muhakemesinden farklıdır. Bu nedenle getStringsFromChildren()işlev, metin düğümlerinin var olup olmadığını belirlemeli ve .nodeValuedeğerleri bir dizide toplamalıdır .

for (var i = 0; i < length; i++) {
    if (nodeList[i].nodeType === Node.TEXT_NODE) {
            textNodes.push(nodeList[i].nodeValue);
    }
}

İkinci örnekte, iki metin düğümü vardır ve getStringFromChildren()her .nodeValueikisini de döndürür ( "\n"). Ancak, filterWhitespaceLines()saf boşluk karakterlerinden oluşan satırları filtrelemek için normal bir ifade kullanır.

nullSatırsonu ( "\n") karakterleri yerine döndürmek istemciye yalan söylemenin / arayan kodun bir biçimi mi? İnsan açısından hayır. DOM açısından evet. Ancak, buradaki sorun metin almak, düzenlemek değil. Çağıran koda dönmek için insan metni yok.

Birinin HTML'sinde kaç tane yeni satır karakterinin görünebileceği asla bilinemez. "İkinci" satırsonu karakterini arayan bir sayaç oluşturmak güvenilmezdir. Var olmayabilir.

Elbette, satırın ilerleyen kısımlarında , fazladan boşluk içeren boş bir öğedeki metni düzenleme sorunu <p></p>(örnek 2), öğenin tam olarak ne olduğunu içerdiğinden emin olmak için bir paragrafın etiketleri arasındaki metin düğümlerinden biri hariç tümünü yok etmek (belki, atlamak) anlamına gelebilir. göstermesi gerekiyordu.

Her şeye rağmen, olağanüstü bir şey yaptığınız durumlar dışında, hangi metin düğümünün .nodeValueözelliğinin düzenlemek istediğiniz gerçek, insan tarafından okunabilir metne sahip olduğunu belirlemenin bir yoluna ihtiyacınız olacaktır . filterWhitespaceLinesbizi oraya götürüyor.

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. 
    }
}

Bu noktada şuna benzer bir çıktı elde edebilirsiniz:

["Dealing with text nodes is fun.", "Some people just use jQuery."]

DOM'da bu iki dizenin birbirine bitişik olacağının garantisi yoktur, bu nedenle bunları birleştirmek .join()doğal olmayan bir bileşik oluşturabilir. Bunun yerine, çağıran kodda, getTextById()hangi dizeyle çalışmak istediğinizi seçmeniz gerekir.

Çıkışı test edin.

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);
}

Bir ekleyebilir .trim()içini getStringsFromChildren()açan ve sonlarındaki boşluk kurtulmak için (ya da (sıfır uzunluğunda dizesine alanlarda bir demet açmak için "")), ama nasıl dize (her uygulama metnine gerçekleşmesi gerekebilir neyi önsel bilebilir Bulunduktan sonra mı? Yapmazsınız, bu yüzden bunu belirli bir uygulamaya bırakın getStringsFromChildren()ve jenerik olsun.

Bu özgüllük düzeyinin ( targetve benzeri) gerekli olmadığı zamanlar olabilir . Bu harika. Bu durumlarda basit bir çözüm kullanın. Bununla birlikte, genelleştirilmiş bir algoritma, basit ve karmaşık durumlara uyum sağlamanıza olanak tanır.


8

İlk # metin düğümü içeriğini döndüren ES6 sürümü

const extract = (node) => {
  const text = [...node.childNodes].find(child => child.nodeType === Node.TEXT_NODE);
  return text && text.textContent.trim();
}

Verimliliği ve esnekliği merak ediyorum. (1) .from()Yüzeysel kopyalanmış bir dizi örneği yapmak için kullanımı . (2) .find()kullanarak bir dizi karşılaştırması yapmak için kullanımı .nodeName. Kullanmak node.NodeType === Node.TEXT_NODEdaha iyi olur. (3) Değer olmadığında boş bir dizge döndürmek, metin düğümü bulunmazsa nulldaha doğrudur . Metin düğümü bulunmazsa, birinin oluşturulması gerekebilir! Boş bir dizge döndürürseniz, bir metin düğümünün var olduğu ve normal şekilde değiştirilebileceği gibi yanlış bir izlenim bırakabilirsiniz. Temelde, boş bir dizge döndürmek beyaz bir yalandır ve en iyisi kaçınılmalıdır. ""
Anthony Rutledge,

(4) Bir nodeList içinde birden fazla metin düğümü varsa, burada hangi metin düğümünü istediğinizi belirlemenin bir yolu yoktur. İlk metin düğümünü isteyebilirsiniz , ancak son metin düğümünü de isteyebilirsiniz .
Anthony Rutledge,

Array.from'u değiştirmek için ne önerirsiniz?
jujule

@Snowman lütfen bu tür önemli değişiklikler için kendi cevabınızı ekleyin veya OP'ye bunları cevaplarına dahil etme fırsatı vermek için tavsiyelerde bulunun.
TylerH

@jujule - HTMLCollection'ı Dizilere [...node.childNodes]dönüştürmek için kullanmak daha iyi
vsync

5

.text() - for jquery

$('.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

1
Bence standart javascript yöntemi 'innerText' olmalıdır
Reporter

2
Bu OP istediği şekilde çalışmıyor - bu içindeki metni alacak açok eleman: jsfiddle.net/ekHJH
James Allardice

1
@James Allardice - Jquery çözümüyle işim bitti şimdi bu işe yarayacak .................
Pranay Rana

Bu neredeyse işe yarayacak, ancak .seçicinizin başlangıcında eksiksiniz, yani aslında titleöğenin metnini alıyorsunuz , öğelerle değilclass="title"
James Allardice

@reporter .innerText, yakın zamanda kabul edilen eski bir IE sözleşmesidir. Standart DOM komut dosyası açısından, node.nodeValuebir metin düğümünün metnini nasıl alırsınız.
Anthony Rutledge,

2

Bu boşlukları da yok sayacaktır, bu yüzden hiçbir zaman boş textNodes..code çekirdek Javascript kullanarak alamazsınız.

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'da kontrol edin: - http://jsfiddle.net/webx/ZhLep/


curNode.nodeType === Node.TEXT_NODEDaha iyi olurdu. Bir döngü içinde dize karşılaştırması ve düzenli ifade kullanmak, özellikle büyüklüğü oDiv.childNodes.lengtharttıkça , düşük performanslı bir çözümdür . Bu algoritma, OP'nin özel sorusunu çözer, ancak potansiyel olarak korkunç bir performans maliyetiyle. Metin düğümlerinin düzeni veya sayısı değişirse, bu çözümün doğru çıktı vereceği garanti edilemez. Başka bir deyişle, tam olarak istediğiniz metin düğümünü hedefleyemezsiniz. HTML yapısının ve içindeki metnin düzenlenmesinin insafına
Anthony Rutledge,

1

Yalnızca text()metin düğümlerini almak için XPath'in düğüm testini de kullanabilirsiniz . Örneğin

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;
}

0

Bu, tüm alt düğümlerin (özyinelemeli) birleştirilmiş metnini içeren bir dizi oluşturmak için ES6'daki çözümüm . Bunun aynı zamanda çocuk düğümlerinin kısa kökünü de ziyaret ettiğini unutmayın.

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');
}

Bu çözüm, https://stackoverflow.com/a/41051238./1300775 çözümünden esinlenmiştir .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.