-2

I need to write a Javascript or (preferably) Typescript function that takes a string of mostly HTML, does something to it, and returns valid HTML. I had the idea that "</>" would be replaced with the correct closing tag, for example:

<button>I'm a button</> I'm text!

would give:

<button>I'm a button</button> I'm text!

And should work with attributes & nested "</>" occurrences too. What I've tried:

const parse = (html: string): string => {
     // regex to match </> tags
    const regex = /<\/>/g;
    // stack to keep track of open tags
    const stack: string[] = [];
  
    // use a parser to parse the HTML and fill the stack with open tags
    const parser = new DOMParser();
    const doc = parser.parseFromString(html, "text/html");
  
    // loop through all nodes and fill the stack with open tag names
    const nodes = doc.documentElement.childNodes;
    for (let i = 0; i < nodes.length; i++) {
        const node = nodes[i];
       
        if (node.nodeType === Node.ELEMENT_NODE) {
            // add the tag name to the stack
            stack.push((node as Element).tagName.toLowerCase());        
        }
    }
  
    // replace </> with corresponding closing tag
    const result = html.replace(regex, () => {
      // get the last open tag from the stack
      const lastOpenTag = stack.pop();
      
      // construct the closing tag
      return `</${lastOpenTag}>`;
    });
    // return the HTML code
    return result
}

However, when trying it, this happened:

input:

<button>I'm a button!</>
I am text!

output:

<button>I'm a button! I am text!</button>

Can you help me fix this function?

Barmar
  • 741,623
  • 53
  • 500
  • 612
Fighter178
  • 303
  • 2
  • 9

1 Answers1

0

I found a kinda solution. It isn't exactly what I was looking for, but it is close enough. I am aware this is very close to PHP, but for my purposes, it is fine. Maybe ill switch the question mark for a hashtag. It allows for this syntax:

<?div>
tag content
</?>

To turn into:

<div>
tag content
</div>

This is the code:

const findUnknownTags = (html:string):string => {
      // I am pretty sure this covers all cases. Correct me if I am wrong.
      const pattern = /<\?(\w+)>((?:(?!<\?|\?>).)*)<\/\?>/g;
      const matches = html.matchAll(pattern);
    
      for (const match of matches) {
        const rawTagName = match[1]
        const tagName = `<${match[1]}>`;
        const tagContent = match[2];
        const closingTag = `</${rawTagName}>`;
    
        html = html.replace(match[0], `${tagName}${tagContent}${closingTag}`);
      }
    
      return html;
    }
}
Fighter178
  • 303
  • 2
  • 9