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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
user610983
  • 855
  • 2
  • 9
  • 14

9 Answers9

172

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';
        }
    }
};
Community
  • 1
  • 1
  • 1
    Does this help? I think he wants to convert the effect of


    to
    . This would have the side effect of converting

    to nothing.
    – Joseph Jun 12 '13 at 12:45
  • 23
    @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. – MatsT Jun 12 '13 at 13:12
  • 14
    This solution can remove some more `
    ` than expected too, if there are no tags in between: http://jsfiddle.net/ahMMv/13/
    – zch Jun 12 '13 at 20:05
  • 2
    What are these new-fangled jigga mahoo's? Things have changed since I was a beginner. – David G Jun 12 '13 at 20:42
  • 2
    @0x499602D2 do you mean the + token in CSS? It's been standard for a while, as has > – jackweirdy Jun 13 '13 at 00:24
  • 2
    @jackweirdy: Indeed, they've been in the standard [since he was 3](http://www.w3.org/TR/1998/REC-CSS2-19980512/selector.html), if he is as young as his profile states. (I feel old now.) – BoltClock Jun 13 '13 at 03:22
  • 3
    (Sorry about the last incorrect comment) Here's a remedy to @zch 's spotted problem: http://jsfiddle.net/ahMMv/17/ – Passerby Jun 13 '13 at 04:57
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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Kaz
  • 55,781
  • 9
  • 100
  • 149
  • 2
    Maybe the multiple `
    ` tags were already created by Javascript.
    – Uooo Jun 13 '13 at 05:53
  • 6
    @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. – Kaz Jun 13 '13 at 06:26
  • 2
    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). – Ben Lee Jun 18 '13 at 21:45
  • 1
    But, I can still see there being valid reasons for client-only manipulation of html outside of control -- it's just not typical. – Ben Lee Jun 18 '13 at 21:50
  • 1
    Wow, no knowledge of the context and has no answer but still needs to say 'its bad design'. – Dalibor Čarapić Jun 19 '13 at 07:04
  • @dcarpic I said it looks like a bad design, not that it is bad design. The context could be such that it isn't good design. Other answers cover the "how" several times over again, also without knowledge of the context. – Kaz Jun 19 '13 at 16:52
9

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 >).

marekful
  • 14,986
  • 6
  • 37
  • 59
  • But how could I ensure it will only work when it found more than 3
    just do the job?
    – user610983 Jun 12 '13 at 09:31
  • It replaces any 2 or more to only 1. – marekful Jun 12 '13 at 09:32
  • But changing the quantifier `+` to `{3,}` as in the other examples will make it work that way. Updated answer... – marekful Jun 12 '13 at 09:34
  • I changed it become jQuery('body').html().replace(/(
    ){3,}/gi, '
    '); but it not work for me
    – user610983 Jun 12 '13 at 09:38
  • Oh, sorry, your problem with that solution is that it should be `$(ducument.body).html( $(document.body).html().replace.....)); Your solution only replaces the text but then doesn't assign the replaced value back to document.body. – marekful Jun 12 '13 at 09:43
  • but if I focus on certain element ID as like this jQuery('#content').html().replace(/(
    ){3,}/gi, '
    '); ?? should be no problem right? But it still not work for me
    – user610983 Jun 12 '13 at 09:51
  • OK, consider this: `text = 'abc def'; text.replace('def', 'ghi');` `text` is now UNCHANGED! But `text = text.replace('def', 'ghi');` Text is now CHANGED. – marekful Jun 12 '13 at 10:17
  • 1
    Simply executing the `.replce()` will NOT change the original value. You must assign the return value of the replace method to the original variable. – marekful Jun 12 '13 at 10:17
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/

  • I don't understand what the following line does : `while (node = node.previousSibling) { if (node.nodeType !== Node.TEXT_NODE || node.nodeValue.trim() !== '') { break; }; }` – Simon May 23 '23 at 17:10
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.

Steve Bennett
  • 114,604
  • 39
  • 168
  • 219
0

Try this

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

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

Demo

Amit
  • 15,217
  • 8
  • 46
  • 68
  • 3
    That solution would break the moment there was something, like say a space or a line break, between the different
    tags.
    – Eivind Eidheim Elseth Jun 12 '13 at 12:51
  • 1
    [Converting DOM to HTML and then back again is a bad idea](http://stackoverflow.com/questions/7392930/why-should-y-innerhtml-x-innerhtml-be-avoided/7393126#7393126). – lonesomeday Jun 12 '13 at 16:47
  • 1
    The best regex for this would be: `.replace(/(
    \s*)+/g, "
    \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 `
    \n`, but his apparrent preference isn't self-closing.
    – Suamere Jun 12 '13 at 18:43
  • @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! – Qantas 94 Heavy Jun 13 '13 at 09:55
  • 2
    @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. – lonesomeday Jun 14 '13 at 20:51
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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Jair Reina
  • 2,606
  • 24
  • 19
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
Blake Stevenson
  • 171
  • 1
  • 6
-1

Try this:

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

DEMO: jsfiddle

UdayKiran Pulipati
  • 6,579
  • 7
  • 67
  • 92
Stephan
  • 41,764
  • 65
  • 238
  • 329
  • don't -1 her! what she suggested is fine only that she replaces all `
    `s with an line break `\n`. change that to `
    ` and you'll be fine.
    – yardarrat Jun 12 '13 at 18:23
  • It's a good effort, but using a regex instead of the DOM guarantees future problems and edge cases. – mskfisher Jun 13 '13 at 17:10