ChatGPT解决这个技术问题 Extra ChatGPT

使用 .text() 仅检索未嵌套在子标签中的文本

如果我有这样的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”。

有没有办法只获取(并可能通过 .text("") 之类的东西删除)标签中的自由文本,而不是其子标签中的文本?

HTML 不是我写的,所以这是我必须使用的。我知道在编写 html 时将文本包装在标签中很简单,但同样,html 是预先编写的。

因为我还没有足够的声誉来发表评论,并且我不希望失去知识(希望它对其他人有所帮助),所以 macio.Jun' answer、正则表达式和 iStranger' answerReplace a textNode with HTML in Javascript? 的组合允许我搜索字符串的纯文本节点,并用链接替换所有出现的地方。

J
Jonathan Arbely

我喜欢这种基于 hereclone() 方法的可重用实现,它只获取父元素内的文本。

提供代码以方便参考:

$("#foo")
    .clone()    //clone the element
    .children() //select all the children
    .remove()   //remove all the children
    .end()  //again go back to selected element
    .text();

使用此解决方案,您只能获得没有孩子的文本,但不能只替换文本。
我没有得到一件事:如果 .end() 回到选定的元素,那么 text() 应该复制带有子元素的原始文本。但在实践中,我看到我们操纵的克隆中的文本正在被复制。所以 end() 回到 clone() ?
这是一种非常低效的方法
@billyonecan,您能建议一种更有效的方法吗?这很吸引人,因为它“干净”和“短”。你有什么建议?
@derekmx271 看看 Stuart's answer
M
Mrchief

简单的回答:

$("#listItem").contents().filter(function(){ 
  return this.nodeType == 3; 
})[0].nodeValue = "The text you want to replace with" 

我不明白为什么有效的答案(不会生成无关的数据结构)没有像看起来不那么可怕的答案那样被投票。 +5 如果可以的话。
简单有效的答案
这不仅更有效而且正确!此解决方案适用于文本分散在子元素之间的情况。 +5
更清楚地说,如果您使用 IE8+,则可以使用 this.nodeType == Node.TEXT_NODE 而不是 this.nodeType == 3。更容易阅读和理解 IMO。
如果您在没有文字的东西上使用它,这将中断。如果您将其用作函数并且有可能有文本也可能没有文本的情况,只需将 .contents().filter(...) 调用捕获到局部变量并检查其长度,例如 var text = $(this).contents().filter(...); if (text.length) { return text[0].nodeValue; } return "";
T
TLS

这对我来说似乎是过度使用 jquery 的一个案例。以下将忽略其他节点获取文本:

document.getElementById("listItem").childNodes[0];

你需要修剪它,但它可以让你在一条简单的线路中得到你想要的。

编辑

以上将获得文本节点。要获取实际文本,请使用以下命令:

document.getElementById("listItem").childNodes[0].nodeValue;

最佳答案,您不应该为此需要插件或 10 个 jQuery 调用链。 $('.foo')[0].childNodes[0].nodeValue.trim()
如果文本内容被分成几个节点(如一系列 crlf、text、crlf)怎么办?是否有任何(rael-life)保证由 ua 构建的 dom 将使用最简单的结构?
完全是最好的答案......为什么其他人有时会过度使用 jQuery?
也许是为了避免不得不照顾跨浏览器的兼容性?
这仅适用于
您想要的文本其他
。它不适用于
other你想要的文本
M
MadScientist

更简单快捷:

$("#listItem").contents().get(0).nodeValue

这个跨浏览器兼容吗?
当然,它检索由索引给出的 jQuery 对象匹配的元素之一:Jquery Docs .get()
@Nate 如果您需要在 <br/> 上使用它标记您可以使用 macio.Jun 的答案。
为什么选择 get(0) 而不仅仅是 [0]
<div id="listItem"> <div>Contents?</div> Text? What text? </div> 失败
D
DUzun

类似于接受的答案,但没有克隆:

$("#foo").contents().not($("#foo").children()).text();

这是一个用于此目的的 jQuery 插件:

$.fn.immediateText = function() {
    return this.contents().not(this.children()).text();
};

以下是如何使用这个插件:

$("#foo").immediateText(); // get the text without children

t.children() 中的 t 是什么?
这是 pbjk 在 15 年 1 月编写的解决方案的重复解决方案……尽管如此 - 它看起来不错。
不是真的,@Oskar。 .contents() 部分在这里很关键!
@AndroidDev您始终可以将选择器替换为适合您的任何东西。这只是为了说明技术!我还添加了一个插件版本,以表明它即使没有 ID 也能工作
你得到我对 jQuery 插件的支持 - 一个非常优雅的解决方案,以最通用和可用的方式解决它。
B
Brent

不是代码:

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

好的。将其作为下一个 jQuery 版本的拉取请求怎么样?
p
pbjk

尝试这个:

$('#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 方法。
@DotNetWala 的答案如下,应该使用而不是这个答案。或者至少,使用 .detach() 方法而不是 .remove()
g
galeksandrp
$($('#listItem').contents()[0]).text()

Stuart answer. 的简短变体

或与 get()

$($('#listItem').contents().get(0)).text()

B
Brave Dolphin
jQuery.fn.ownText = function () {
    return $(this).contents().filter(function () {
        return this.nodeType === Node.TEXT_NODE;
    }).text();
};

感谢您提供此代码片段,它可能会提供一些即时帮助。正确解释would greatly improve其教育价值,通过展示为什么这是一个很好的问题解决方案,并将使其对未来有类似但不相同的问题的读者更有用。请edit您的回答以添加解释,并说明适用的限制和假设。
i
inarilo

如果文本节点的位置 index 在其兄弟节点中是固定的,则可以使用

$('parentselector').contents().eq(index).text()

r
rotaercz

这是一个老问题,但最佳答案非常低效。这是一个更好的解决方案:

$.fn.myText = function() {
    var str = '';

    this.contents().each(function() {
        if (this.nodeType == 3) {
            str += this.textContent || this.innerText || '';
        }
    });

    return str;
};

只需这样做:

$("#foo").myText();

m
mvmn

我认为这也是一个很好的解决方案 - 如果您想获取所有文本节点的内容,这些节点是所选元素的直接子节点。

$(selector).contents().filter(function(){ return this.nodeType == 3; }).text();

注意:jQuery 文档使用类似的代码来解释内容功能:https://api.jquery.com/contents/

PS 还有一种更丑陋的方法来做到这一点,但这更深入地展示了事情是如何工作的,并允许在文本节点之间自定义分隔符(也许你想要在那里换行)

$(selector).contents().filter(function(){ return this.nodeType == 3; }).map(function() { return this.nodeValue; }).toArray().join("");

g
gaetanoM

我建议使用 createTreeWalker 来查找所有未附加到 html 元素的文本元素(此函数可用于扩展 jQuery):

函数 textNodesOnlyUnder(el) { var resultSet = [];变量 n = 空; 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; } 返回 NodeFilter.FILTER_SKIP; }, false); while (n = treeWalker.nextNode()) { resultSet.push(n); } 返回结果集; } window.onload = function() { var ele = document.getElementById('listItem'); var textNodesOnly = textNodesOnlyUnder(ele); var resultsText = textNodesOnly.map(function(val, index, arr) { return 'Text element N. ' + index + ' --> ' + val.textContent.trim(); }).join('\n') ; document.getElementById('txtArea').value = 结果文本; }

  • 这是一些文本 第一个跨度文本 第二个跨度文本

  • M
    Marcus Parsons

    在 IE 9+ 兼容的语法中使用纯 JavaScript 只需几行:

    const childNodes = document.querySelector('#listItem').childNodes;
    
    if (childNodes.length > 0) {
        childNodesLoop:
        for (let i = 0; i < childNodes.length; i++) {
            //only target text nodes (nodeType of 3)
            if (childNodes[i].nodeType === 3) {
                //do not target any whitespace in the HTML
                if (childNodes[i].nodeValue.trim().length > 0) {
                    childNodes[i].nodeValue = 'Replacement text';
                    //optimized to break out of the loop once primary text node found
                    break childNodesLoop;
                }
            }
        }
    }
    

    一些注意事项:我不会将该变量称为 children,因为元素的 children 属性是其子 元素 的列表。我会使用 childNodes 来避免混淆。此外,不需要标签,只有一个循环,break 会破坏正确的东西。此外,“主要文本”的东西似乎来自左侧字段,OP 没有提到出于任何原因提前停止。
    变量重命名听起来不错,但我为 for 循环使用了一个标签,只是因为我想看看我正在打破哪个循环,尤其是当中间有文本时。标记 for 循环不会影响性能。在原始帖子中,它说“一个文本节点”,这使得任何人都可以推断出该列表中只有一个元素是目标。如果只有一种替换的可能性,则破坏速度更快。
    正如您所说,为清楚起见标记它没有害处。 :-)
    M
    Mif.ComicVN

    这对我来说是个好方法

       var text  =  $('#listItem').clone().children().remove().end().text();
    

    这与 DotNetWala 的 answer 完全相同。
    Y
    Yu Jiang Tham

    我想出了一个特定的解决方案,它应该比克隆和修改克隆更有效。此解决方案仅适用于以下两个保留,但应该比当前接受的解决方案更有效:

    您只得到文本您要提取的文本在子元素之前

    话虽如此,这里是代码:

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

    J
    Jeach

    就像这个问题一样,我试图提取文本以便对文本进行一些正则表达式替换,但是我的内部元素(即:<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]] 等标记。

    这个简短的代码摘录将帮助任何试图做问题的人......还有更多。


    O
    OG Sean

    不确定您需要它涵盖的灵活性或多少情况,但对于您的示例,如果文本总是出现在第一个 HTML 标记之前 - 为什么不在第一个标记处拆分内部 html 并采用前者:

    $('#listItem').html().split('<span')[0]; 
    

    如果你需要更广泛的可能只是

    $('#listItem').html().split('<')[0]; 
    

    如果您需要两个标记之间的文本,例如在一件事之后但在另一件事之前,您可以执行类似(未经测试)的操作并使用 if 语句使其足够灵活以具有开始或结束标记或两者兼而有之,同时避免空引用错误:

    var startMarker = '';// put any starting marker here
    var endMarker = '<';// put the end marker here
    var myText = String( $('#listItem').html() );
    // if the start marker is found, take the string after it
    myText = myText.split(startMarker)[1];        
    // if the end marker is found, take the string before it
    myText = myText.split(endMarker)[0];
    console.log(myText); // output text between the first occurrence of the markers, assuming both markers exist.  If they don't this will throw an error, so some if statements to check params is probably in order...
    

    我通常为此类有用的东西制作实用程序函数,使它们无错误,然后在可靠后经常依赖它们,而不是总是重写这种类型的字符串操作并冒空引用等风险。这样,您可以重用该函数在许多项目中,永远不必再浪费时间调试为什么字符串引用有未定义的引用错误。可能不是有史以来最短的 1 行代码,但是在您拥有实用程序功能之后,从那时起它就是一行。请注意,大多数代码只是处理存在或不存在的参数以避免错误:)

    例如:

    /**
    * Get the text between two string markers.
    **/
    function textBetween(__string,__startMark,__endMark){
        var hasText = typeof __string !== 'undefined' && __string.length > 0;
        if(!hasText) return __string;
        var myText = String( __string );
        var hasStartMarker = typeof __startMark !== 'undefined' && __startMark.length > 0 && __string.indexOf(__startMark)>=0;
        var hasEndMarker =  typeof __endMark !== 'undefined' && __endMark.length > 0 && __string.indexOf(__endMark) > 0;
        if( hasStartMarker )  myText = myText.split(__startMark)[1];
        if( hasEndMarker )    myText = myText.split(__endMark)[0];
        return myText;
    }
    
    // now with 1 line from now on, and no jquery needed really, but to use your example:
    var textWithNoHTML = textBetween( $('#listItem').html(), '', '<'); // should return text before first child HTML tag if the text is on page (use document ready etc)
    

    如果您需要替换文本,只需使用 $('#listItem').html( newHTML );其中 newHTML 是一个已经具有精简文本的变量。
    D
    Dexter

    Live demo

    <li id="listItem">
        This is some text
        <span id="firstSpan">First span text</span>
        <span id="secondSpan">Second span text</span>
    </li>
    
    <input id="input" style="width: 300px; margin-top: 10px;">
    
        <script type="text/javascript">
    $("#input").val($("#listItem").clone().find("span").remove().end().text().trim());
        //use .trim() to remove any white space
        </script>
    

    D
    Dh. Yaduvanshi

    给菜鸟的:

    我更喜欢 @DUzun's answer,因为它比公认的答案更易于理解且更有效。但它只对我部分有用,因为你不能像这样使用类选择器直接传递元素

    $(".landing-center .articlelanding_detail").get(0).immediateText() //gives .immediateText is not a function error
    

    或这个

    $(".landing-center .articlelanding_detail")[0].immediateText() //gives .immediateText is not a function error
    

    因为一旦您使用 [index] 或 .get(index) 从 $() 函数中提取原生元素,您就会失去 jQuery 对象方法的可链接性,如 here 所述。而且大多数解决方案仅在 id 的上下文中,对于具有类选择器的元素多次使用并不是那么优雅。

    所以,我写了 jQuery 插件:

    $.fn.mainText = function(x=0) {
        return $.trim(this.eq(x).contents().not(this.eq(x).children()).text().replace(/[\t\n]+/g,' '));
    };
    

    这将返回元素的文本,无论是否将 ids 或 class 用作不包括子元素的选择器。还将删除任何 \t or \n 以获得干净的字符串。像这样使用它:

    情况1

    $("#example").mainText(); // get the text of element with example id
    

    案例2

    $(".example").mainText(); // get the text of first element with example class
    

    案例3

    $(".example").mainText(1); // get the text of second element with example class and so on..
    

    T
    T.J. Crowder

    我不会为此烦恼 jQuery,尤其是那些对元素进行不必要的克隆的解决方案。您只需要一个简单的循环抓取文本节点。在现代 JavaScript 中(在撰写本文时——“现代”是一个移动的目标!)并从结果的开头和结尾修剪空白:

    const { childNodes } = document.getElementById("listItem");
    let text = "";
    for (const node of childNodes) {
        if (node.nodeType === Node.TEXT_NODE) {
            text += node.nodeValue;
        }
    }
    text = text.trim();
    

    现场示例:

    const { childNodes } = document.getElementById("listItem");让文本=“”; for (const node of childNodes) { if (node.nodeType === Node.TEXT_NODE) { text += node.nodeValue; } } 控制台日志(文本);

  • 这是一些文本 第一个跨度文本 第二个跨度文本
  • 有些人会为此使用 reduce。我不是粉丝,我认为简单的循环更清晰,但这种用法确实会在每次迭代时更新累加器,所以它实际上并没有滥用 reduce

    const { childNodes } = document.getElementById("listItem");
    const text = [...childNodes].reduce((text, node) =>
        node.nodeType === Node.TEXT_NODE ? text + node.nodeValue : text
    , "").trim();
    

    const { childNodes } = document.getElementById("listItem"); const text = [...childNodes].reduce((text, node) => node.nodeType === Node.TEXT_NODE ? text + node.nodeValue : text , "").trim();控制台.log(文本);

  • 这是一些文本 第一个跨度文本 第二个跨度文本
  • 或者不创建临时数组:

    const { childNodes } = document.getElementById("listItem");
    const text = Array.prototype.reduce.call(childNodes, (text, node) =>
        node.nodeType === Node.TEXT_NODE ? text + node.nodeValue : text
    , "").trim();
    

    const { childNodes } = document.getElementById("listItem"); const text = Array.prototype.reduce.call(childNodes, (text, node) => node.nodeType === Node.TEXT_NODE ? text + node.nodeValue : text , "").trim();控制台.log(文本);

  • 这是一些文本 第一个跨度文本 第二个跨度文本

  • P
    Paul Verschoor

    使用额外的条件来检查 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");
        }
    })
    })
    

    http://jsfiddle.net/7RSGh/


    M
    Marion Go

    为了能够修剪结果,请像这样使用 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()
    好的,这是有道理的。哈哈。谢谢!
    这与 DotNetWala 的 answer 几乎相同。您所做的只是将 .trim() 添加到末尾。这个答案有必要吗?
    D
    Dorjan

    只需将其放在 <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>
    

    我无法控制将自由文本放入标签中,因为我正在处理的代码不是由我创建的。如果我可以只抓取该文本,我可以删除它并用它周围的标签替换它,或者做任何我想做的事情。但同样,html 已经预先编写好了。
    喔好吧。然后我认为您将不得不过滤结果:S对不起。
    a
    achakravarty

    你可以试试这个

    alert(document.getElementById('listItem').firstChild.data)
    

    S
    Sudheera

    我不是 jquery 专家,但是怎么样,

    $('#listItem').children().first().text()
    

    如果您注意到 jquery 专家,那么为什么不先阅读其他答案来成为专家呢?...其中一个恰好与您所写的几乎相同,下面的评论解释了为什么它不是一个好主意。
    E
    El Guapo

    这未经测试,但我认为你可以尝试这样的事情:

     $('#listItem').not('span').text();
    

    http://api.jquery.com/not/


    因为它与 $('#listItem').text() 相同。 #listItem 不是 <span>,因此添加 not('span') 不会做任何事情。