HTML→Markdown Conversion: What Structurally Gets Lost
Tools that convert HTML to Markdown generally fall into two camps: those built on a library like Turndown.js, and those that walk tags directly with the browser's DOMParser and map rules by hand. Either way, they run into the same underlying problem — Markdown (CommonMark) syntax has far less expressive power than HTML, so any element without a matching syntax simply disappears or breaks during conversion. This guide takes apart the actual code behind the HTML→Markdown converter and lays out, with concrete input/output examples, exactly what survives structurally and what falls apart.
1. Merged table cells: colspan/rowspan are ignored entirely
This tool's table conversion function grabs every row's <th>/<td> elements via querySelectorAll and extracts text only. The colspan/rowspan attributes are never read anywhere in the code. The real issue isn't simply "merges get ignored" — it's that the header row and data rows end up with mismatched cell counts. The following input makes it obvious.
<table> <tr><th colspan="2">Name</th><th>Age</th></tr> <tr><td>John</td><td>Smith</td><td>28</td></tr> </table>
The header row is treated as 2 <th> cells (Name, Age), so it's read as a 2-column table — but the data row lists 3 <td> cells as-is (John, Smith, 28). There's no logic to reconcile the header column count with the body column count, so the output Markdown ends up with mismatched columns like this:
| Name | Age || --- | --- || John | Smith | 28 |Since the data row has more pipe (|) characters than the header, the table can render broken or with its last column cut off depending on the GFM renderer.
In short, the safe approach for any table with merged cells is to un-merge it before conversion (or duplicate the merged value into each individual cell).
2. Semantic tags: meaning is dropped, only text survives
Markdown (CommonMark) has no inline formatting syntax beyond emphasis (**bold**, *italic*) and strikethrough (GFM's ~~strikethrough~~). Looking at the source, <mark> (highlight), <abbr> (abbreviation), <cite> (citation), <small>, and <sub>/<sup> (subscript/superscript) are all mapped straight to childrenToMd(node), which just returns the child text. So <mark>important</mark> becomes plain "important" with no highlight, and even H2O written with <sub> becomes plain "H2O" with no subscript. Because the CommonMark standard itself has no subscript/superscript syntax, this isn't a limitation unique to this tool — it's a limitation shared by the whole Markdown ecosystem.
3. Styling and attributes: class/id/style/data-* are never read in the first place
The conversion function only ever references the tag name (tagName), href/src/alt, and class for detecting the language of code blocks. Inline style, class/id, data-*, aria-*, and event handlers (onclick, etc.) are never referenced anywhere in the traversal logic, so there's no path for them to reach the output at all. Think of it this way: converting design-heavy HTML (card layouts, color accents, etc.) strips away all visual information and leaves only the content skeleton.
4. The exception: nested lists actually survive well
Nested lists are exactly where regex-based converters usually fail — but here, the listToMd() function recursively handles any ul/ol found among an li's children and accumulates 2 spaces of indentation per level of recursion depth. You can verify in the code that 3-level nested lists convert correctly. While information gets lost in tables and semantic tags, list structure itself survives relatively safely, thanks to the DOM-traversal approach — a notable exception.
5. A side effect: javascript: links get automatically neutralized
The link conversion code includes a branch that returns plain text instead of link syntax whenever href.startsWith('javascript'). This looks less like a deliberate security design and more like a pragmatic call of "there's no point carrying a link like this over into Markdown" — but as a side effect, malicious links using the javascript: scheme end up automatically stripped out during conversion.
Frequently Asked Questions
Q. How do I convert a table with merged cells without it breaking?
A. Before converting, either unmerge the colspan/rowspan in the HTML and duplicate the value into each individual cell, or manually fix up the column count in the output Markdown table afterward. This tool never reads merge information at all, so don't expect automatic correction.
Q. Is formatting other than bold/italic (underline, highlight, subscript) preserved?
A. No. CommonMark/GFM has no syntax for underline, highlight, or subscript/superscript, so those tags survive only as plain text with no formatting. If you need that formatting, either keep the original HTML as-is, or manually re-insert raw HTML tags into the Markdown after conversion.
Q. Does CSS-based design (cards, badges, colors) carry over in the conversion?
A. No. The class/id/style attributes are never referenced by the conversion logic at all, so only content (text, links, lists, table structure) survives — all visual styling information is lost.
Q. Are nested lists converted safely?
A. Yes. This tool recursively traverses lists and precisely calculates indentation (2 spaces per level), so multi-level nested <ul>/<ol> structures come through intact, unlike tables and semantic tags.