Home>

Help, please, with the solution of the problem.

Problem: The React component accepts a string entered by the user as input. The string contains plain text, newlines, links. At the output, you need to get the same string, with hyphens preserved, and the links must be wrapped in the "a" tag. At first sight quite trivial task, but I faced difficulty.

My algorithm for solving the problem:

  1. Turn the resulting string into an array by splitting the string word by word.
  2. Map through the array and check each element with a regular expression: if the word is a valid url, then wrap it in "a", if not, then wrap it in "span".
  3. The output is an array of words, each of which is wrapped in "span" or "a".

The disadvantage of this solution is that here I create a separate home node for each word, and also lose line breaks (i.e. the output text after rendering will go "solid", even if the user inserted line breaks, i.e. .because I trim them when creating an array).

My solution on repl: https://repl.it/repls/IllegalMiserlyUlyssesbutterfly

const message= 'and here is the url https://superurl.com and here is \n not the url!http://wwwkokoko.ru';
const decorateMessage= message=> {
    const messageBlock= message.replace(/\r|\n/g, ' ').split(' ');
    const regExp= /^(http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&
%@!\-\/]))?/;
    return messageBlock.map(m=> {
        return regExp.test(m)
          ? `<a className='link' href="${m}" target="_blank">${m}&
nbsp;</a>`
          : `<span className='muted'>${m}&
nbsp;</span>`
    })
};
console.log(
  'result: ', decorateMessage(message)
);

I would like to get just one text node from the input data, with line breaks preserved and links wrapped in "a", if there are any inside this node. Everything seems to be simple, but it does not occur to me how such a problem is solved in other tools.

Thanks in advance for your help.

Don't want to accept an answer?

Qwertiy2022-01-14 22:29:00
  • Answer # 1

    Quotes are not handled because it is generally not clear what to do with them. Formally, url cannot contain non-escaped quotes, so it would be possible to break off the address on them, on the other hand, the logic associated with punctuation marks before quotes raises questions: it can be left or vice versa removed, assuming that the url is not enclosed in quotes from the point of view of natural language , namely for correct grouping (for example, the address ends with a dot). So the corresponding characters must be added to the regular expression on their own in this area:[.,?!;:()]*(?:\s|$))[^\s].

    class WithLinks extends React.Component {
      render() {
        varres= []
        this.props.text &
    &
     this.props.text.replace(/((?:https?:\/\/|ftps?:\/\/|\bwww\.)(?:(?![.,?!;:()] *(?:\s|$))[^\s]){2,})|(\n+|(?:(?!(?:https?:\/\/|ftp:\/\/| \bwww\.)(?:(?![.,?!;:()]*(?:\s|$))[^\s]){2,}).)+)/gim, ( m, link, text)=> {
          res.push(link ? <a href={(link[0]==="w" ? "//" : "") + link} key={res.length}>{link}</a> :text)
        })
        return <div className="user-text">{res}</div>  }
    }
    class App extends React.Component {
      state= { text: "Perhaps http://site.rf? Well, or http://site.rf?id=67 -it seems to be parsed\nWell, not without www.google.ru." }
      render() {
        return (
          <div>        <textarea value={this.state.text} onChange={e=> this.setState({ text: e.target.value })} />        <WithLinks text={this.state.text} />      </div>    )
      }
    }
    ReactDOM.render(<App />, document.getElementById("root"));
    textarea {
      width: 100%
      box-sizing: border-box;
      resize: vertical;
      height: 40vh;
    }
    .user-text {
      white-space: pre-wrap;
    }
    <script src="//cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script><script src="//cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script><main id=root></main>

    The markup looks like this:

    So what's the downside?

    Qwertiy2022-01-14 18:24:12
  • Answer # 2

    Quotes are not handled because it is generally not clear what to do with them. Formally, url cannot contain non-escaped quotes, so it would be possible to break off the address on them, on the other hand, the logic associated with punctuation marks before quotes raises questions: it can be left or vice versa removed, assuming that the url is not enclosed in quotes from the point of view of natural language , namely for correct grouping (for example, the address ends with a dot). So the corresponding characters must be added to the regular expression on their own in this area:[.,?!;:()]*(?:\s|$))[^\s].

    class WithLinks extends React.Component {
      render() {
        varres= []
        this.props.text &
    &
     this.props.text.replace(/((?:https?:\/\/|ftps?:\/\/|\bwww\.)(?:(?![.,?!;:()] *(?:\s|$))[^\s]){2,})|(\n+|(?:(?!(?:https?:\/\/|ftp:\/\/| \bwww\.)(?:(?![.,?!;:()]*(?:\s|$))[^\s]){2,}).)+)/gim, ( m, link, text)=> {
          res.push(link ? <a href={(link[0]==="w" ? "//" : "") + link} key={res.length}>{link}</a> :text)
        })
        return <div className="user-text">{res}</div>  }
    }
    class App extends React.Component {
      state= { text: "Perhaps http://site.rf? Well, or http://site.rf?id=67 -it seems to be parsed\nWell, not without www.google.ru." }
      render() {
        return (
          <div>        <textarea value={this.state.text} onChange={e=> this.setState({ text: e.target.value })} />        <WithLinks text={this.state.text} />      </div>    )
      }
    }
    ReactDOM.render(<App />, document.getElementById("root"));
    textarea {
      width: 100%
      box-sizing: border-box;
      resize: vertical;
      height: 40vh;
    }
    .user-text {
      white-space: pre-wrap;
    }
    <script src="//cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script><script src="//cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script><main id=root></main>

    The markup looks like this:

    So what's the downside?

    Qwertiy2022-01-14 18:24:12
  • Answer # 3

    You can consider everything as a link, up to the first whitespace character:

    document.querySelectorAll('[parseLinks]')
      .forEach(el=> {
        el.innerHTML= el.innerHTML.replace(
          /\b(https?\:\/\/\S+)/mg,
          '<a href="$1">$1</a>'
        );
      });
    <div parseLinks>  Link: http://example.org/
    </div>

    This will not work here, unfortunately, because I do not work directly with the house. This is React, just js. In fact, I need to return an element of a virtual home. Although, it may be possible to refine this solution to my situation ...

    Dmitry Esin2022-01-14 09:31:24

    Well, remove the work with the DOM, what's the problem? replace -one line

    vp_arth2022-01-14 09:31:24

    Well, so-so on the DOM'u to climb in react ...

    Qwertiy2022-01-14 18:09:48

    @Qwertiy, what does react have to do with it? or DOM at what here? The response contains a simple syntax construct with a regular expression. The DOM is used as a demonstration of how this construct works. I did not offer anyone to drag it as-is anywhere.

    vp_arth2022-01-14 18:24:12

    @vp_arth, react at question tags.

    Qwertiy2022-01-14 18:24:12
  • Answer # 4

    You can consider everything as a link, up to the first whitespace character:

    document.querySelectorAll('[parseLinks]')
      .forEach(el=> {
        el.innerHTML= el.innerHTML.replace(
          /\b(https?\:\/\/\S+)/mg,
          '<a href="$1">$1</a>'
        );
      });
    <div parseLinks>  Link: http://example.org/
    </div>

    This will not work here, unfortunately, because I do not work directly with the house. This is React, just js. In fact, I need to return an element of a virtual home. Although, it may be possible to refine this solution to my situation ...

    Dmitry Esin2022-01-14 09:31:24

    Well, remove the work with the DOM, what's the problem? replace -one line

    vp_arth2022-01-14 09:31:24

    Well, so-so on the DOM'u to climb in react ...

    Qwertiy2022-01-14 18:09:48

    @Qwertiy, what does react have to do with it? or DOM at what here? The response contains a simple syntax construct with a regular expression. The DOM is used as a demonstration of how this construct works. I did not offer anyone to drag it as-is anywhere.

    vp_arth2022-01-14 18:24:12

    @vp_arth, react at question tags.

    Qwertiy2022-01-14 18:24:12