52

How do I use JavaScript to detect

<br> <br> <br> 

to become one

<br> 

?

I tried with:

jQuery('body').html().replace(/(\<br\>\r\n){3, }/g,"\n"); 

but this is not working for me.

9
  • 2
    I'm surprised no one suggested using a regex :( Commented Jun 12, 2013 at 12:29
  • 6
    @LescaiIonel Really? Read the answers. But anyway we have the DOM so why bother with regex? (Some days we'll see Tony the pony.) Also, replacing the HTML will cause the browser to reparse the HTML which is not efficient. Commented Jun 12, 2013 at 12:34
  • 5
    If you don't want to have multiple <br>, why are they there in the first place? Commented Jun 12, 2013 at 12:40
  • 2
    How did this question and answer get so many views and votes in such a short timespan...? Commented Jun 12, 2013 at 16:02
  • 3
    @mskfisher: What's more curious is how this question made it to Hot Questions in the first place. Commented Jun 13, 2013 at 3:07

9 Answers 9

173

CSS Solution

If you want to disable the effect of multiple <br> on the page, you can do it by CSS without using JavaScript:

br + br { display: none; } 

However, this method is ideal when you are working with tags, something like this:

<div>Text</div><br /><br /><br /> <div>Text</div><br /><br /><br /> <div>Text</div><br /><br /><br /> 

In other cases, like this:

Hello World<br /> <br /> Hello World<br /> <br /> Hello World<br /> <br /> 

It will fail (as CSS passes text nodes). Instead, use a JavaScript solution.


JavaScript Solution

// It's better to wait for document ready instead of window.onload(). window.onload = function () { // Get all `br` tags, defined needed variables var br = document.getElementsByTagName('br'), l = br.length, i = 0, nextelem, elemname, include; // Loop through tags for (i; i < l - 1; i++) { // This flag indentify we should hide the next element or not include = false; // Getting next element nextelem = br[i].nextSibling; // Getting element name elemname = nextelem.nodeName.toLowerCase(); // If element name is `br`, set the flag as true. if (elemname == 'br') { include = true; } // If element name is `#text`, we face text node else if (elemname == '#text') { // If text node is only white space, we must pass it. // This is because of something like this: `<br /> <br />` if (! nextelem.data.replace(/\s+/g, '').length) { nextelem = br[i+1]; include = true; } } // If the element is flagged as true, hide it if (include) { nextelem.style.display = 'none'; } } }; 
Sign up to request clarification or add additional context in comments.

7 Comments

@Joseph The definition of "br + br" is a br element that is placed directly after another br element. It only matches one element, not two at the same time. The selector will match all consecutive br elements except for the first.
This solution can remove some more <br> than expected too, if there are no tags in between: jsfiddle.net/ahMMv/13
What are these new-fangled jigga mahoo's? Things have changed since I was a beginner.
@0x499602D2 do you mean the + token in CSS? It's been standard for a while, as has >
(Sorry about the last incorrect comment) Here's a remedy to @zch 's spotted problem: jsfiddle.net/ahMMv/17
|
10

Simpler:

var newText = oldText.replace(/(<br\s*\/?>){3,}/gi, '<br>');

This will allow optional tag terminator (/>) and also spaces before tag end (e.g. <br /> or <br >).

8 Comments

But how could I ensure it will only work when it found more than 3 <br > just do the job?
It replaces any 2 or more to only 1.
But changing the quantifier + to {3,} as in the other examples will make it work that way. Updated answer...
I changed it become jQuery('body').html().replace(/(<br\s*\/?>){3,}/gi, '<br>'); but it not work for me
Simply executing the .replce() will NOT change the original value. You must assign the return value of the replace method to the original variable.
|
10

What is the point of sending HTML, which is in a form that you don't want, to the client browser and making it run JavaScript code to clean it up? This looks like a bad design.

How about fixing all your static HTML, and HTML generation, so that these superfluous <br> elements do not occur in the first place?

If you use JavaScript to modify the document object, do so for dynamic effects that cannot be achieved in any other way.

6 Comments

Maybe the multiple <br> tags were already created by Javascript.
@w4rumu Or maybe the HTML came from a different site. Javascript isn't always a package with a particular web site. There could be Javascript running in the browser as a browser extension, editing HTML for the user.
While in general your advice is good (so +1), it doesn't directly answer the question. Sometimes you have to deal with html where you have absolutely no control over who generated it or why it was generated that way -- in which case direct solutions to questions like this become beneficial and things like "fix it to begin with" become impossible. (On the other hand, usually these kinds of transformations of html you don't have control over happen on the server side anyway, in which case they can be fixed on that side -- javascript is probably the wrong place to be doing this in any case).
But, I can still see there being valid reasons for client-only manipulation of html outside of control -- it's just not typical.
Wow, no knowledge of the context and has no answer but still needs to say 'its bad design'.
|
2

This solution is jQuery + DOM only, does not manipulate HTML as string, works with text nodes, ignores whitespace only text nodes:

$('br').each(function () { const {nodeName} = this; let node = this; while (node = node.previousSibling) { if (node.nodeType !== Node.TEXT_NODE || node.nodeValue.trim() !== '') { break; }; } if (node && node !== this && node.nodeName === nodeName) { $(node).remove(); } }); 

See: https://jsfiddle.net/kov35jct/

1 Comment

I don't understand what the following line does : while (node = node.previousSibling) { if (node.nodeType !== Node.TEXT_NODE || node.nodeValue.trim() !== '') { break; }; }
1

Wouldn't something like this be the right approach:

$("br~br").remove() 

EDIT: No, it's wrong, because its definition of "contiguous" is too loose, as per BoltClock.

4 Comments

To elaborate, that is the jQuery "next siblings" selector: api.jquery.com/next-siblings-selector
This kills the second <br> in <br><hr><br><hr>, despite the <br> elements not being contiguous. Depending on the use case, this may not be desirable.
You're much too polite. Thanks. :)
@BoltClock The adjacent selector should be used instead, right?
0

Try this

$('body').html($('body').html().replace(/(<br>)+/g,"<br>")); 

It will replace n number of <br> into one.

Demo

5 Comments

That solution would break the moment there was something, like say a space or a line break, between the different <br> tags.
The best regex for this would be: .replace(/(<br\s*/?>\s*)+/g, "<br>\n") it handles all sorts of breaks with all sorts of spacing. Also, the replace also breaks the line afterward. I'd suggest replacing with <br />\n, but his apparrent preference isn't self-closing.
@lonesomeday: You'd need to loop through each element, which I'd ought to think would be slower than converting to a string form of HTML, manipulating the string and then changing it back into a DOM form - although I wouldn't pass up on an opportunity to see an implementation of that!
@Qantas94Heavy The problem is more that you lose information by doing that. E.g. any event handlers bound within body will be removed. This is not a good idea to my mind.
0

I would go with this:

$('body').html($('body').html().replace(/<br\W?\\?>(\W?(<br\W?\\?>)+)+/g,"<br>")); 

However, after reading the comments in another post here I do consider that you should try to avoid doing this in case you can correct it in the back end.

Comments

0

A lot of the other answers to this question will only replace up to certain amount of elements, or use complex loops. I came up with a simple regex that can be used to replace any number of <br> tags with a single tag. This works with multiple instances of multiple tags in a string.

/(<br>*)+/g 

To implement this in JavaScript, you can use the String.replace method:

myString.replace(/(<br>*)+/g, "<br/>"); 

To replace multiple <br/> tags, add a / to the regex:

/(<br\/>*)+/g 

Comments

-1

Try this:

jQuery('body').html( jQuery('body').html().replace(/(?:<br>\s+){3,}/ig,"\n")); ); 

DEMO: jsfiddle

2 Comments

don't -1 her! what she suggested is fine only that she replaces all <br>s with an line break \n. change that to <br> and you'll be fine.
It's a good effort, but using a regex instead of the DOM guarantees future problems and edge cases.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.