简体   繁体   中英

Find text between two tags/nodes

I thought that this would be rather straightforward, but I think the keywords are just too general so I keep getting query results for things like this and this .

Basically, I have the following HTML:

<div id="test">
    Lorem
    <span class="highlighted">ipsum</span>
    dolor sit amet,
    <span class="highlighted">consectetur</span>
    <span class="highlighted">adipiscing</span>
    elit. Sed massa.
<div>

I'd like to merge adjacent span tags into a single span tag, which means finding spans with only whitespace between them (this could include spaces, tabs and newlines).

The result I'm looking for is this:

<div id="test">
    Lorem
    <span class="highlighted">ipsum</span>
    dolor sit amet,
    <span class="highlighted">consectetur adipiscing</span>
    elit. Sed massa.
<div>

I've examined the nextUntil function, but it seems to only return tags, not text. The result, for example, of

$("span.highlighted").nextUntil("span.highlighted").andSelf().text();

is

ipsumconsecteturadipiscing

rather than

ipsum dolor sit amet, consecteturadipiscing

Given two tags, how can I find the text between them?

Dropping down to the DOM lets you see text node contents when checking siblings.

Something like:

function combineSpans(span, nextspan)
{
  var follower = span.nextSibling;
  var concat = true;

   while (follower != nextspan)
   {
     if (follower.nodeName != '#text')
     {
       concat = false;
       break;
     }

     var len = follower.data.trim().length;
     if (len > 0)
     {
       concat = false;
       break;
     }

     follower = follower.nextSibling;
   }

  if (concat)
  {
    $(span).text($(span).text() + " " + $(follower).text());
    $(follower).remove();
  }
}

Using this with your HTML in this CodePen .

Well, you can try this...

At least it works perfect when using 2 spans to merge them like your example (when an "empty" element is present). Otherwise, you will need to think a little to handle the span that lasts.

(To check what I am talking about just take a look commenting the last line: nextElem.remove() and check the new div html) .

Live Demo: http://jsfiddle.net/oscarj24/t45MR/


HTML:

<div id="test">
    Lorem
    <span class="highlighted">ipsum</span>
    dolor sit amet,
    <span class="highlighted">consectetur</span>
    <span class="highlighted">adipiscing</span>
    elit. Sed massa.
</div>

jQuery:

$(document).ready(function () {

    var elem = $('#test');

    elem.contents().filter(function(index) {
        //Get index of an empty element
        if($.trim($(this).text()) === '') 
            //Merge the previous index span with the next index span texts
            mergeSpan(index);
    });

    //Print new inner html
    alert(elem.html());
});

function mergeSpan(index){

    //Get all 'div' elements
    var elems = $('#test').contents();

    //Get previous and next element according to index
    var prevElem = elems.eq(index - 1);
    var nextElem = elems.eq(index + 1);

    //Concat both texts
    var concatText = prevElem.text() + ' ' + nextElem.text();

    //Set the new text in the first span
    prevElem.text(concatText);
    //Remove other span that lasts
    nextElem.remove();
};

Result:

<div id="test">
    Lorem
    <span class="highlighted">ipsum</span>
    dolor sit amet,
    <span class="highlighted">consectetur adipiscing</span>
    elit. Sed massa.
<div>

I know you have already accepted a solution, but I wanted to take the challenge to provide a pure javascript solution which can be incorporated into your toolset. Here's what I came up with, and would like any help to make this better.

http://jsfiddle.net/ryanwheale/JhZPK/

function joinNeighborsByClassName( className ) {
    var items = document.getElementsByClassName(className),
        next = null,
        remove = [],
        append = '',
        i = 0;

    while( i < items.length && (next = items[i++]) ) {
        while( (next = next.nextSibling) && next !== null ) {
            if((next.nodeType === 3  && /^\s+$/.test(next.nodeValue)) ||     
               (new RegExp("(?:^|\s)" + className + "(?!\S)", "g")).test(next.className) ) {
                append += (next.innerHTML || next.nodeValue);

                if(next.nodeType !== 3) {
                    remove.push(next);
                }
            } else {
                break;
            }
        }

        if(append) items[i-1].innerHTML += append;
        for(var n = 0; n < remove.length; n++) {
            remove[n].parentNode.removeChild(remove[n]);
        }

        remove = [];
        append = '';
    }
}

joinNeighborsByClassName('highlighted');

For your last question "Given two tags, how can i find the text between them?"

Well, I have this solution for you.

var divData = $("#test").html(); // Getting html code inside div

Now, using preg_match() you can obtain the text between two words, in your case the text between spans, like this:

preg_match('/'.preg_quote($word1).'(.*?)'.preg_quote($word2).'/is', $html, $matches);

$word1 = '<span class="highlighted">';
$word2 = '<';
$html = $_POST['divData']; // Via post/get you will have to send the html code gotten in "var divData"

and for each match(with a for cycle) concat em in a variable adding whitespaces between them. Then do an echo your result and in your call back function add it to your div

This link could help you in how make a POST call in jquery jquery post

As the title request it, here's a possible way to get text nodes between spans:

var textNodes=$('#test').contents().filter(function(){
    return this.nodeType == 3; // text node
});

It is also possible to manually check for consecutive spans that have no empty text node between them by comparing each node with the precedent one. Something like this will do the trick:

function combineSpansIn(selector, spanClass) {
    // initialize precedent values
    var prec=null;
    var precNodeType;

    $(selector).contents().each(function(){
        if ($.trim( $(this).text() ) !== "") { // empty nodes will be useless here
            var nodeType = this.nodeType;

            // check if still a combinable span
            if (nodeType == 1 && this.className==spanClass && nodeType == precNodeType) {
                // append current node to precedent one
                $(prec).append(" "+ $(this).text() );

                // remove current node
                $(this).remove();
            } else {
                // update precedent values
                prec=this;
                precNodeType = nodeType;
            }
        }
    });
}

combineSpansIn('#test', 'highlighted');

Please take a look at this FIDDLE .

Heh . . . looks like Oscar Jara and I came up with similar ideas around using the JQuery .contents() method, but ended up with some considerably different implementations:

$(document).ready(function () {
    $("#testDiv").contents().each(function() {
        var prevNode = this.previousSibling;
        var fillerText = "";
        while ((prevNode) && ($.trim($(prevNode).text()) === "")) {
            fillerText += prevNode.nodeValue;
            prevNode = prevNode.previousSibling;
        }

        if ((prevNode) && (this.nodeType === 1) && (prevNode.nodeType === 1)) {
            $(prevNode).text($(prevNode).text() + fillerText + $(this).text());
            $(this).remove();
        }
    });
});

I tested a few different sets of HTML data (three spans back-to-back, spans with spaces in between and without, etc.) all based on your original code, and it seems to work . . . the key was to skip over any "whitespace only" text nodes in between the <span> tags, while preserving any needed spacing that they may have contained.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM