-1

I've tried this

<html>
<head>
<title>None</title>
</head>
<body>
<p id="text">just some random text. random text</p>
<button type="button" onclick="strReplace();">Replace</button>

<script>
 function strReplace(){
        var myStr = document.getElementById("text");
        var mySte = myStr.textContent;
        console.log(mySte);
</script>

</body>

and I want this following outcome

just some random text
random text

  • Your code doesn't attempt any sort of replacement, it just logs the current text - what have you tried? There's also a syntax error - the `strReplace()` function doesn't have a closing bracket – Luke Briggs Jul 24 '21 at 14:23
  • 1
    this might work: `document.getElementById('text').innerHtml = document.getElementById('text').innerText.replace('.','
    ')`
    – TJBlackman Jul 24 '21 at 14:25
  • 1
    @TJBlackman it's innerHTML and .replace will by default only replace the first one - you'd need to use `/\./g` :) – Luke Briggs Jul 24 '21 at 14:26

2 Answers2

1

You can use this regex to find and replace string without breaking html: /(?!<[^>]+)\.(?![^<]+>)/g

[myattr]
{
  background-color: pink;
}
[myattr]:after
{
  content: "this element's attribute is: " attr(myattr);
  background-color: lightgreen;
  margin: 1em;
}
p > span
{
  background-color: lightblue;
}
<html>
<head>
<title>None</title>
</head>
<body>
<p id="text">just some. random text. <span myattr="text.with.dots">nested.html</span> end.
<span>i < 40. But i is also > 30. What are valid values of i?</span>
</p>
<button type="button" onclick="strReplace();">Replace</button>

<script>
 function strReplace(){
        var myStr = document.getElementById("text");
        myStr.innerHTML = myStr.innerHTML.replace(/(?!<[^>]+)\.(?![^<]+>)/g, "<br>");
        console.log(myStr.innerHTML);
 }
</script>

</body>
vanowm
  • 9,466
  • 2
  • 21
  • 37
  • [HTML isn't a regular language](https://stackoverflow.com/a/1732454/2873896) so can't be parsed with a regular expression. "without breaking HTML" is therefore not true; more literally answering the question with `/\./g` and innerText is probably a safer bet. – Luke Briggs Jul 24 '21 at 14:37
  • if you replace just \. you might replace in the attributes of elements. I've update code that demonstrated this. – vanowm Jul 24 '21 at 14:39
  • "and innerText" - thus this :) – Luke Briggs Jul 24 '21 at 14:40
  • using innerText in this case is useless too, as it won't allow inject html, it will also break any nested html – vanowm Jul 24 '21 at 14:42
  • Use the DOM - loop over childNodes, finding text nodes, and replace specifically in them. – Luke Briggs Jul 24 '21 at 14:42
  • The original point is HTML isn't part of the question - it is just implied in the code. – Luke Briggs Jul 24 '21 at 14:49
  • I'm pretty sure having "
    " in the question would imply html, not just text...
    – vanowm Jul 24 '21 at 14:59
  • In the output yes, not the input. – Luke Briggs Jul 24 '21 at 14:59
  • For example if the input comes from a textarea, which is probably will do, your answer assumes it is actually HTML and will do it wrong; if it _is_ HTML, it treats HTML as a regular language which is also wrong. – Luke Briggs Jul 24 '21 at 15:03
  • As I said, feel free spend your time actually answering question the way you feel it should be done, rather than hypothesizing about what OP meant. – vanowm Jul 24 '21 at 15:03
  • OP spent 0 time googling replace in JS, so will also have just copy-pasted your code and will probably never come back here, completely unaware that it is unreliable. – Luke Briggs Jul 24 '21 at 15:05
  • I still don't see any reliable answers here...But now I'm curious, can you give me an example when this will break html and fail. (Just for the record, I'm not trying say you are wrong about the proper way of doing by going through text nodes, it's just the simplest) – vanowm Jul 24 '21 at 15:08
  • `"i < 40. But i is also > 30. What are valid values of i?"` - is valid HTML. A tag soup parser implementing the HTML specification knows what to do here. Your code misses the first . – Luke Briggs Jul 24 '21 at 15:17
  • No it's not. You can't use `<` in html it must be encoded as < – vanowm Jul 24 '21 at 15:19
  • Try it: `i < 40. But i is also > 30. What are valid values of i?` - will load in any standard compliant browser. – Luke Briggs Jul 24 '21 at 15:20
  • Just added your string directly into the answer. still doesn't break the html – vanowm Jul 24 '21 at 15:20
  • That particular scenario gets "normalised" by a browser. With any sort of HTML handling, assuming that something else will normalise it first is of course not always the case; this could be on node.js etc. – Luke Briggs Jul 24 '21 at 15:23
  • Yes, and? It doesn't fail because browser fixes invalid html. Now if you'd use this regex on a plain text (aka html input from an ajax request, before it was parsed by browser), than yes, it will fail. But now we are going beyond the OP question, aren't we? – vanowm Jul 24 '21 at 15:28
  • and: it makes the code unreliable; code should aim to be flawless, especially on a public forum where it can be copied into all sorts of software. Maybe the input came from a textarea, maybe the code is running on a server where there isn't something normalising it via the DOM load -> innerHTML steps. We know nothing about people's input. – Luke Briggs Jul 24 '21 at 15:32
  • So far you've suggested it should've been used `innerText` as per OPs code, which will break HTML if there is any. Or manipulating text nodes, which requires parsing HTML, which ultimately make this answer still reliable...so, please, feel free to provide a more reliable code. Thank you. – vanowm Jul 24 '21 at 15:53
  • `manipulating text nodes, which requires parsing HTML, which ultimately make this answer still reliable` - Manipulating text nodes is a DOM thing, not a HTML thing. It occurs after a valid tag soup parser has parsed the HTML. HTML fundamentally cannot be safely handled with a regex, because it is not a regular language. Your answer uses a regex on assumed HTML. – Luke Briggs Jul 24 '21 at 16:20
0

To directly answer the question:

var outputHtml = inputText.replace(/\./g, '<br />');

The Javascript replace method will by default replace the first instance of something (the first . in this case), so the g regex modifier tells it to replace them all. The \. in the regex is because . is a special character in regexes. This will replace every dot in the text.

What about ellipsis?

This technique won't work well on text that contains literal ellipsis, like this:

Hello world...

If your text is likely to contain this, and you want that to be ignored, then the following regex is more appropriate:

/[^\.](\.)[^\.]/g

This'll match any . which is not surrounded by other .

var outputHtml = inputText.replace(/[^\.](\.)[^\.]/g, '<br />');

Handling HTML

In the question here, the input is actually coming from the DOM. We can't replace . on its innerHTML as it would replace content inside HTML tags as well. So, if your input text is coming from the DOM like this (and not, say, a textarea), then the safest route is to apply the replacement only to text nodes. That is done like this:

document.getElementById("start").addEventListener("click", () => {
    var inputNode = document.getElementById("text");
    replace(inputNode);
});

function replace(node) {
    if(!node) {
        return;
    }
    
    if (node.nodeName == '#text') {
        // We've found a text node. Apply the regex to it now.
        // Note that you can use either of the above regexes here.
        var inputText = node.textContent;
        var lines = inputText.split(/\./g);
    
        if(lines.length > 1) {
            // It had at least one dot in it. 
            // Swap in this new set, each with a <br /> between them.
            var parent = node.parentNode;
            var nextSibling = node.nextSibling;
            parent.removeChild(node);
            
            lines.forEach((line, i) => {
                if(i != 0){
                    // insert the <br>
                    var br = document.createElement('br');
                    parent.insertBefore(br, nextSibling);
                }
                var textNode = document.createTextNode(line);
                parent.insertBefore(textNode, nextSibling);
            });
        }
    } else {
        // Loop through each child node.
        // We go backwards such that completed replacements don't affect the loop.
        for(var i=node.childNodes.length - 1; i>=0; i--) {
            replace(node.childNodes[i]);
        }
    }
}
<html>
  <head>
    <title>None</title>
  </head>
  <body>
    <p id="text">just some random text. random text</p>
    <button type="button" id="start">Replace</button>
  </body>
</html>

If you're starting from a HTML string inside a browser, you can then use the above replace method and the browsers internal parsing to safely only affect the actual text:

function replaceHtmlString(html) {
    var div = document.createElement('div');
    div.innerHTML = html;
    replace(div);
    return div.innerHTML;
}
Luke Briggs
  • 3,745
  • 1
  • 14
  • 26