admin管理员组

文章数量:1394611

I wish to test in jQuery for the existence of a <br /> as the very first, or very last element (including text nodes) within a paragraph. The 2 examples below would be true:


<p>
    <br />
    Lorem ipsum dolor sit amet consectetur, adiscipling elit.
</p>


<p>
    Lorem ipsum dolor sit amet consectetur, adiscipling elit.
    <br />
</p>

While this example would be false:


<!-- This is false -->
<p>
    Lorem ipsum dolor sit amet consectetur, adiscipling elit
    <br />
    Lorem ipsum dolor sit amet consectetur, adiscipling elit.
</p>

If true, the following class’s of diog-fail brbr are to be applied to the containing paragraph element.

I wish to test in jQuery for the existence of a <br /> as the very first, or very last element (including text nodes) within a paragraph. The 2 examples below would be true:


<p>
    <br />
    Lorem ipsum dolor sit amet consectetur, adiscipling elit.
</p>


<p>
    Lorem ipsum dolor sit amet consectetur, adiscipling elit.
    <br />
</p>

While this example would be false:


<!-- This is false -->
<p>
    Lorem ipsum dolor sit amet consectetur, adiscipling elit
    <br />
    Lorem ipsum dolor sit amet consectetur, adiscipling elit.
</p>

If true, the following class’s of diog-fail brbr are to be applied to the containing paragraph element.

Share Improve this question asked Feb 6, 2011 at 17:26 DigiKevDigiKev 1,0713 gold badges11 silver badges19 bronze badges 3
  • Does it have to be text nodes, or can you change the markup so the text is in, say, a <span>? It's typically much easier to ignore text nodes. – Matt Ball Commented Feb 6, 2011 at 17:41
  • Yes, it has to be text nodes. This is a diagnostic test to check for correct usage of line breaks within an HTML document. – DigiKev Commented Feb 6, 2011 at 17:43
  • 1 ...also, unless you remove the white space in your "true" examples, those do actually have text nodes (the white space) as first/last elements, respectively. – Matt Ball Commented Feb 6, 2011 at 17:53
Add a ment  | 

8 Answers 8

Reset to default 3

This is easy using universally supported DOM properties. jQuery isn't much use here.

function isBr(el) {
    return el && el.nodeType == 1 && el.tagName == "BR";
}

function isWhitespaceNode(node) {
    return node.nodeType == 3 && /^\s*$/.test(node.data);
}

function isFirstOrLastChildBr(el) {
    var first = el.firstChild;
    if (isWhitespaceNode(first)) first = first.nextSibling;
    if (isBr(first)) return true;

    var last = el.lastChild;
    if (isWhitespaceNode(last)) last = last.previousSibling;
    if (isBr(last)) return true;

    return false;
}

So if your <p> element had id "foo":

var $foo = $("#foo");
if ( isFirstOrLastChildBr($foo[0]) ) {
    $foo.addClass("diog-fail brbr");
}

UPDATE WITH EXAMPLE FOR MULTIPLE ELEMENTS

To apply this to a set of matched elements:

// All paragraphs
$("p").filter(function() {
    return isFirstOrLastChildBr(this);
}).addClass("diog-fail brbr");

As per the test cases you have provided, this will work:

$('p').each(function ()
{
    var $this = $(this),
        $brs = $this.children('br'),
        numBRs = $brs.length,
        firstBR = $brs.get(0),
        firstBRprev = firstBR ? firstBR.previousSibling : null,
        firstBRnext = (firstBR && numBRs == 1) ? firstBR.nextSibling : null,
        lastBR = numBRs > 1 ? $brs.get(numBRs) : null,
        lastBRnext = lastBR ? lastBR.nextSibling : null;

    if ((firstBRprev && !firstBRprev.nodeValue.trim()) ||
        (firstBRnext && !firstBRnext.nodeValue.trim()) ||
        (lastBRnext && !lastBRnext.nodeValue.trim()))
    {
        console.log(this);
        $this.addClass('diog-fail brbr');
    }
});

Not much jQuery because jQuery hides lots of the nasty DOM business from you, including text nodes.

Demo →

Here is the jsfiddle:

Using RegEx and If Else condition

    $('p').each(function() {
        var temp = this.innerHTML.replace(/^\s*|\s(?=\s)|\s*$/g, "").toLowerCase();
        if (temp.substring(0, 5) == '<br/>' || temp.substring(0, 4) == '<br>' || temp.substring(0, 6) == '<br />') {
            $(this).addClass('diog-fail brbr');
            console.log('start: true');
        } else if (temp.substring(temp.length - 5, temp.length) == '<br/>' || temp.substring(temp.length - 4, temp.length) == '<br>' || temp.substring(temp.length - 6, temp.length) == '<br />') {
            $(this).addClass('diog-fail brbr');
            console.log('end: true');
        } else {
            console.log('none: ' + false);
        }
    });

First get all paragraphs and then use RegEx on the innerHTML and flatted it by removing whitespace, newline and etc. Then do if else condition to see if first and last few substrings matches <br>, <br/> or <br />

How about:

function brIsFirstOrLast(selector) {
    var contents = $(selector).contents().filter(function() {
        return this.nodeType !== 3 || $.trim($(this).text().replace("\n", "")) !== "";
    });

    var last = contents.last()[0];
    var first = contents.first()[0];

    return (!!last.tagName && last.tagName.toLowerCase() === "br") ||
        (!!first.tagName && first.tagName.toLowerCase() === "br");
}

Usage:

brIsFirstOrLast("p:first");

Note that this function expects the selector to return one result. You could probably add some more validation too, but this should get you started.

See it working: http://jsfiddle/andrewwhitaker/F2dwm/

I would suggest using a little bit of RegExp:

//returns true if there is a <br/>   at begin or end, otherwise false 
$.trim($('p').html()).match(/(^<br\s*\/?>|<br\s*\/?>$)/i)

Otherwise you will have to struggle with browser-dependencies on handling whitespaces as textNodes

Okay, here's one (only slightly clunky) way to do it, given the following html for demo purposes:

html

<div class="testThis">
    <p>Some text, or other with a 'br' at the end.<br /></p>
</div>

<div class="testThis">
    <p><br />Some more text, with a 'br' at the beginning.</p>
</div>

<div class="testThis">
    <p>Some text with a 'br' <br /> in the middle</p>
</div>

css

.testThis {
    border: 1px solid #ccc;
    margin: 0 auto 1em auto;
    padding: 0.5em;
    width: 80%;
    border-radius: 1em;
}

.brFirst {
    border-color: #f00;
}

.brLast {
    border-color: #00f;
}

jQuery

$('.testThis').each(
    function(){
        var haystack = $(this).find('p').html();
        var needle = '<br>';

        //alert(haystack.indexOf('<br>'));
        if (haystack.indexOf(needle) == 0) {
            $(this).addClass('brFirst');
        }
        else if (haystack.indexOf(needle) == (haystack.length - needle.length)) {
            $(this).addClass('brLast');
        }
    });

JS Fiddle demo.


Edited to update the jQuery, to use the trim() method (so whitespace at the beginning, and end, of the variable haystack will be removed):

$('.testThis').each(
    function(){
        var haystack = $(this).find('p').html().trim();
        var needle = '<br>'; // Tested in Chrome the `<br />` element is converted to `<br>`

        if (haystack.indexOf(needle) == 0) {
            $(this).addClass('brFirst');
        }
        else if (haystack.indexOf(needle) == (haystack.length - needle.length)) {
            $(this).addClass('brLast');
        }
    });

Updated JS Fiddle demo.

For removing <br> from inside <p> in embedded editor, I use:

target.find('p').find('br').each(function () {
    if(! $(this)[0].nextSibling || ! $(this)[0].previousSibling) $(this).remove();
    });

For test and change <p> class:

target.find('p').each(function () {
    let ancestor = $(this);
    $(this).find('br').each(function () {
        console.log(! $(this)[0].nextSibling || ! $(this)[0].previousSibling ? 'edge' : '!edge');
        ancestor.addClass('zorglubissime');
    });
});

I hope this helps...

Ooops. This better:

target.find('p').each(function () {
    let ancestor = $(this);
    $(this).find('br').each(function () {
        if(! $(this)[0].nextSibling || ! $(this)[0].previousSibling)
            ancestor.addClass('zorglubissime');
    });
});

Use the :first-child and :last-child selectors

So $('p > br:first-child, p > br:last-child').length > 0

本文标签: