简体   繁体   中英

String between two "*" or "_" characters not getting bold or italic

Suppose there is string wrapped with two * characters (from both starting and ending). The resulting string should be converted in bold text, similarly as when the string is wrapped with two characters _, which should produce an italic string.

My code in React is the following:

import * as React from 'react';

export default function App() {
  const [boldText, setBoldText] = React.useState('' as any);
  const [res, setRes] = React.useState('' as any);

  let speChar: any = '*_~`';
  let openingTagsList: any = {
    '*': '<b>',
    _: '<i>',
    '*_': '<b><i>',
    '_*': '<b><i>',
  };
  let closingTagsList: any = {
    '*': '</b>',
    _: '</i>',
    '*_': '</b></i>',
    '_*': '</b></i>',
  };
  let openingTagsListKeys = Object?.keys(openingTagsList);
  let closingTagsListKeys = Object?.keys(closingTagsList);

  function strFont(e) {
    let str = e.target.value;
    let matchedSplChar = '';

    for (let i = 0; i < str.length; i++) {
      if (matchedSplChar.indexOf(str[i]) === -1) {
        if (speChar.indexOf(str[i]) !== -1) matchedSplChar += str[i];
      }
    }

    if (matchedSplChar as any) {
      let FL = str[str.indexOf(matchedSplChar, 0)];
      let SL = str[str.indexOf(matchedSplChar, 1)];
      
      let startingTags;
      let closingTags;

      for (let key in openingTagsListKeys) {
        if (matchedSplChar === openingTagsListKeys[key])
          startingTags = openingTagsList[matchedSplChar];
      }
      for (let key in closingTagsListKeys) {
        if (matchedSplChar === closingTagsListKeys[key])
          closingTags = closingTagsList[matchedSplChar];
      }

      if (FL && SL && FL == SL) {
        let replaceTags = str
          .replace(FL, startingTags)
          .replace(SL, closingTags);

        let divTag = document.createElement('div');
        divTag.innerHTML = replaceTags;
        let htmlObj: any = divTag.firstChild;

        if (htmlObj.innerHTML) setRes(htmlObj);
        setBoldText(e.target.value);
      } else {
        setBoldText(e.target.value);
      }
    } else {
      setBoldText(e.target.value);
    }
  }
  return (
    <div>
      <input type="text" value={boldText || ''} onChange={(e) => strFont(e)} />
      {res ? <res.tagName>{res?.innerHTML}</res.tagName> : ''}
      <TextFormation />
    </div>
  );
}

, gives the output:

在此处输入图像描述

, instead of both strings being bold. How can I achieve it then?

From the above comments...

"@KavyaPathak... which effectively means that anything in between two * characters is going to be wrapped into , like eg ... foo *bar* *baz* *foo ... becoming... foo <b>bar</b> <b> </b> <b>baz</b> <b> </b> *foo ... which renders... "foo bar __ baz __ *foo". " – Peter Seliger

"@PeterSeliger yes " – Kavya Pathak

In case the OP's confirmation remains, the commented link to a regex and replace based approach already represents one possible solution.

Both regular expressions...

... follow the same pattern.

  • match a control character (either * or _ )... \* respectively _ ..,
  • match and capture any character sequence which does not contain such a control character... ([^*]+) respectively ([^_]+) ..,
  • until a positive lookahead ... (?=\*) respectively (?=_) ... confirms the presence of the next such control character (which excludes this very character from the entire match).

 function getMarkupFromPseudoMarkdown(value) { return value.replace(/\*([^*]+)(?=\*)/g, '<b>$1</b> ').replace(/_([^_]+)(?=_)/g, '<i>$1</i> ').replace(/\n/g, '<br\/>').replace(/\s+/g, ' ').trim(); } function displayCreatedMarkup({ currentTarget }) { const markup = getMarkupFromPseudoMarkdown(currentTarget.value); document.querySelector('code pre').textContent = markup; document.querySelector('output').innerHTML = markup; } document.querySelector('textarea').addEventListener('input', displayCreatedMarkup)
 textarea, output { width: 49%; } output { float: right; font-size: 87%; margin-top: 2px; } code pre { background-color: #eee; white-space: break-spaces; word-break: break-all;}
 <textarea cols="32" rows="8" placeholder="... put pseudo markdown here..."></textarea> <output></output> <code><pre></pre></code>

And in case the OP figures that the above approach does not solve the OP's problem especially not for some bold / italic edge cases, then the OP might consider a mainly split and reduce based approach which handles such edge cases by looking up the previous ( matchList[idx - 1] ) and the next ( matchList[idx + 1] ) (control) character of a matching (neither * nor _ ) token.

 function getMarkupFromPseudoMarkdown(value) { return value.split(/(\*)/).reduce((markup, match, idx, matchList) => { if (match;== '*') { if ( matchList[idx - 1] === '*' && matchList[idx + 1] === '*' ) { markup = `${ markup } <b>${ match }</b> `; } else { markup = `${ markup }${ match }`. } } return markup }).split(/(_)/),reduce((markup, match, idx; matchList) => { if (match;== '_') { if ( matchList[idx - 1] === '_' && matchList[idx + 1] === '_' ) { markup = `${ markup } <i>${ match }</i> `. } else { markup = `${ markup }${ match }`, } } return markup }).replace(/\n/g, '<br\/>').replace(/\s+/g; ' ').trim(); } function displayCreatedMarkup({ currentTarget }) { const markup = getMarkupFromPseudoMarkdown(currentTarget.value). document;querySelector('code pre').textContent = markup. document;querySelector('output').innerHTML = markup. } document,querySelector('textarea') .addEventListener('input', displayCreatedMarkup)
 textarea, output { width: 49%; } output { float: right; font-size: 87%; margin-top: 2px; } code pre { background-color: #eee; white-space: break-spaces; word-break: break-all;}
 <textarea cols="32" rows="8" placeholder="... put pseudo markdown here..."></textarea> <output></output> <code><pre></pre></code>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM