jQuery nextUntil include text nodes

2020-02-06 02:24发布

问题:

I'm using nextUntil method to get all stuff between two elements. But this method does not include text nodes to output. It gives an array like [<br>, <br>, <br>]. How can I get all stuff including text nodes?

This is the HTML code:

$('.content a:contains("spoiler").b:even').each(function() {
  $(this).nextUntil('.content a:contains("spoiler").b')
    .wrapAll('<div style="border:solid 1px black;"></div>');
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="content">
  --- <a class="b" href="/?q=spoiler">spoiler</a> ---
  <br>
  <br> dangerous text here
  <br> --- <a class="b" href="/?q=spoiler">spoiler</a> ---
  <br> safe text here
  <br> --- <a class="b" href="/?q=spoiler">spoiler</a> ---
  <br>
  <br> dangerous text here
  <br> --- <a class="b" href="/?q=spoiler">spoiler</a> ---
</div>

JSFiddle: http://jsfiddle.net/Lwk97rvq/1/

回答1:

Only the jQuery .contents() method returns all nodes (including text nodes, normally ignored).

So maybe something like this?:

http://jsfiddle.net/ykv3gf5L/2/

$('.content').each(function () {
    var open = false;
    var result = $();
    $(this).contents().each(function () {
        var $this = $(this);
        if ($this.text() == "spoiler") {
            if (open) {
                result.wrapAll('<div style="border:solid 1px black;"></div>');
                open = false;
            } else {
                result = $();
                open = true;
            }
        } else {
            result = result.add($this)
        }
    });
    if (open) {
        result.wrapAll('<div style="border:solid 1px black;"></div>');
    }
});

It just iterate all nodes and based on a flag starts a new collection, or wraps the nodes found.

The final if (open) allows for an unclosed spolier block within a content classed div.

Notes:

  • $() is an empty jQuery collection (like an empty array but for jQuery objects)
  • I suggest you use a style for your spoilers and use a class e.g. result.wrapAll('<div class="spoiler"></div>');

e.g. http://jsfiddle.net/ykv3gf5L/3/



回答2:

You can create your own jquery plugin which does the same as nextUntil but includes text nodes:

$.fn.nextUntilWithTextNodes = function (until) {
    var matched = $.map(this, function (elem, i, until) {
        var matched = [];
        while ((elem = elem.nextSibling) && elem.nodeType !== 9) {
            if (elem.nodeType === 1 || elem.nodeType === 3) {
                if (until && jQuery(elem).is(until)) {
                    break;
                }
                matched.push(elem);
            }
        }
        return matched;
    }, until);

    return this.pushStack(matched);
};

So you can call this nextUntilWithTextNodes instead of nextUntil and you are good to go.