Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

jQuery.replaceWith, changing <br /><br /> to </p>?

I am working with a CMS that produces double break tags in place of my preferred paragraph tag. I tried:

$('<br /><br />').replaceWith('</p>');

Now after you finish laughing; could you please let me know if this is possible and if yes, the best way to approach this.

Update (from OP comment):
The paragraphs are not empty they have a <p> tag at the start and the last para closes with a </p> the paragraphs in between are spaced with the double <br> tag.

I have no access to the editor code, which is my problem. It was custom build and awful.

like image 337
Laurence L Avatar asked Oct 11 '11 08:10

Laurence L


2 Answers

  1. Get all the <br> nodes. Optionally, ignore the ones directly inside a <p> block.
  2. Then, for each <br> node, wrap any text nodes that immediately precede or immediately follow it.
  3. Finally, delete the <br> node.

This approach handles nesting and doesn't trash links, the page layout, etc.

The code:

$('.your_cms_container br').each ( function () {
    if (this.parentNode.nodeName != "P") {
        $.each ([this.previousSibling, this.nextSibling], function () {
            if (this.nodeType === 3) { // 3 == text
                $(this).wrap ('<p></p>');
            }
        } );

        $(this).remove (); //-- Kill the <br>
    }
} );


Update:

After editing the OP's question, I realized that I hadn't quite addressed his specific situation. (I use the above code, to great effect in userscripts.)

He wants to change this:

<p> "Paragraph 1" <br />
    <a href="http://xkcd.com/246/">Link, the first</a>
    Line after single break.
    <br /><br />
    "Paragraph 2"
</p>

into this:

<p> "Paragraph 1" <br />
    <a href="http://xkcd.com/246/">Link, the first</a>
    Line after single break.
</p>
<p> "Paragraph 2" </p>


The difficulties being: You don't want to trash nested elements (if any) or event handlers (if any).

For this, use the following approach:

  1. Grab just the offending paragraphs with a selector like p:has(br+br). Beware that the adjacent selector ignores text nodes -- the very thing we don't want it to do, in this case.
  2. Loop through each offending paragraph's contents, one by one.
  3. Use a state machine to create a new paragraph at each <br><br> and to move the content nodes to the appropriate <p>.

The code looks like this. You can see it in action at jsFiddle:

var badGraphs   = $("#content p:has(br+br)");
var targetP     = null;
var justSplit   = false;

badGraphs.each ( function () {
    var parentP = $(this);

    parentP.contents ().each ( function (J) {
        if (justSplit) {
            justSplit = false;
            // Continue the loop. Otherwise, it would copy an unwanted <br>.
            return true;    
        }
        var thisjNode   = $(this);
        var nextjNode   = $(this.nextSibling);

        if (thisjNode.is ("br")  &&  nextjNode.is ("br") ) {
            thisjNode.remove (); //-- Kill this <br>
            nextjNode.remove (); //-- and the next

            //-- Create a new p for any following content
            targetP     = targetP || parentP;
            targetP.after ('<p></p>');
            targetP     = targetP.next ("p");

            justSplit   = true;
        }
        else if (targetP) {
            //-- Move the node to its new home.
            targetP.append (this);
        }
    } );
} );
like image 29
Brock Adams Avatar answered Oct 31 '22 11:10

Brock Adams


There are a couple of ways you could try this. The preferable one (since you're already using jQuery) is probably using contents():

$('.your_cms_container').contents().filter(function() {
    return this.nodeType == 3;  // Only text nodes
}).wrap('<p></p>')

This will wrap all text nodes in paragraph tags. Follow that by ripping out all of the
tags:

$('.your_cms_container').filter('br').remove();

Of course it gets more complicated if your text nodes aren't all simple children of the main text container.

The other (dirty) option is simply to mangle the html manually and then put it back in to jQuery once you're done:

var htmlAsString = $('.your_cms_container').html();    // Get the contents as simple text

... // Do nasty things to the string here

$('.your_cms_container').html(htmlAsString);    // Put it back and let jQuery try and sort out any mess

I'm not saying the second way is wrong and sometimes it's the only way to get things done without rewriting everything. But it still makes me feel dirty if I have to do it ;-)

like image 119
Jason Avatar answered Oct 31 '22 11:10

Jason