18

On the SO post https://stackoverflow.com/questions/46677752/the-difference-between-requirex-and-import-x#:~:text=require%20uses%20module.,more%20or%20less%20like%20module.

The footer style is incorrect and crowds out the body content: screenshot of style bug

4

3 Answers 3

11

The issue is caused by the HTML returned by the server for this answer. In particular, the HTML sanitization occurring server side is sanitizing div tags incorrectly (as in, the way that it is being done is incorrect, not that it is incorrect to do so), as an example, the markdown:

|  | `import` (ES Modules syntax) | `require` (CommonJS Modules syntax) |
|:---:|:---|:---|
| **Exports** | <div><p>content</p><p>morecontent</p> For example: <pre><code>code</code></pre></div> | <div><p>content</p>For example: <pre><code>code2</code></pre></div> |

Generates the HTML:

<div class="s-prose js-post-body" itemprop="text">
    <div class="s-table-container">
        <table class="s-table">
            <thead>
                <tr>
                    <th style="text-align: center;"></th>
                    <th style="text-align: left;"><code>import</code> (ES Modules syntax)</th>
                    <th style="text-align: left;"><code>require</code> (CommonJS Modules syntax)</th>
                </tr>
            </thead>
            <tbody>
                <tr>
                    <td style="text-align: center;"><strong>Exports</strong></td>
                    <td style="text-align: left;"><p>content</p><p>morecontent</p> For example: <pre><code>code</code></pre>
    </div>
                    </td>
                    <td style="text-align: left;"><p>content</p>For example: <pre><code>code2</code></pre></td>
                </tr>
            </tbody>
        </table>
</div>

This stray </div> causes a number of issues I can't explain, potentially with more issues server side, resulting in the footer falling inside the main #container of the page, among other things.


In the case of this answer, the markdown for every table cell (bar headers, the first column and the first row) has a div within them that contains all of the content, removing those divs resolves the issue.

Bad source:

<!-- language-all: javascript -->
|  | `import` (ES Modules syntax) | `require` (CommonJS Modules syntax) |
|:---:|:---|:---|
| **Summary** | <p>It is the latest standard for working with modules in JavaScript and is supported in modern browsers and environments that transpile or support ES6, like TypeScript or Babel.</p> | <p>It was not originally part of JavaScript, but was adopted as the standard for Node.js, which has been routinely used in JavaScript server-side development.<br><br>While Node.js historically used CommonJS, it now also supports ES6 modules.</p> |
| **Exports** | <div><p>Static (pre-defined). The structure of the module's exports is determined when the code is parsed, not while running.</p><p>This static nature allows tooling such as bundlers and linters to analyze the code without executing it, enabling features like better tree-shaking and faster load times in browsers.</p> For example: <pre><code>// myModule.js&#13;export function myFunc() { /\*...\*/ }&#13;export const MY_CONST = 123;</code></pre></div> | <div><p>Computed during runtime. The exports in a module are determined during the execution of the code.</p>For example: <pre><code>// myModule.js&#13;if (process.env.NODE_ENV === 'development') {&#13;  module.exports.debug = function debug() {&#13;    console.log('Debugging...');&#13;  }&#13;} else {&#13;  module.exports.log = function log() {&#13;    console.log('Logging...');&#13;  }&#13;}</code></pre></div> |
| **Loading Modules** | <div><p>Can be asynchronous, allowing efficient, selective loading of module parts. This can result in faster load times and better performance.</p>For example: <pre><code>import { myFunc } from './myModule.js';&#13;&#13;myFunc();</code></pre></div> | <div><p>Synchronous (loads modules one by one). Always loads entire module, which could affect performance if the module is large.</p>For example: <pre><code>const { debug, log } = require('./myModule.js');&#13;&#13;if(debug) debug();&#13;if(log) log();</code></pre></div> | 
| **Full Example** |  <div>Make sure to export the function first. <pre><code>// somefile.js&#13;export function sayHello() {&#13;  console.log("Hello, world!");&#13;}&#13;&#13;console.log("somefile has been loaded!");</code></pre> Then import it <pre><code>// main.js&#13;import { sayHello } from './somefile.js';&#13;&#13;sayHello();&#13;&#13;// πŸ‘‡ Outputs πŸ‘‡&#13;// "somefile has been loaded!"&#13;// "Hello, world!"</code></pre></div> |  <div>Make sure to add the function to `module.exports`. <pre><code>// somefile.js&#13;function sayHello() {&#13;  console.log("Hello, world!");&#13;}&#13;&#13;module.exports = { sayHello };&#13;&#13;console.log("somefile has been loaded!");</code></pre> Then import it <pre><code>// main.js&#13;const { sayHello } = require('./somefile.js');&#13;&#13;sayHello();&#13;&#13;// πŸ‘‡ Outputs πŸ‘‡&#13;// "somefile has been loaded!"&#13;// "Hello, world!"</code></pre></div> |
| **Scope** | <div><p>If an exported value changes in the module it was defined in, that change is visible in all modules that import this value.<p>For example: <pre><code>// somefile.js&#13;let count = 1;&#13;export { count };&#13;&#13;setTimeout(() => count = 2, 1000);</code></pre> Now use it somewhere <pre><code>// main.js&#13;import { count } from './somefile.js';&#13;&#13;console.log(count); // 1&#13;setTimeout(() => console.log(count), 1000); // 2</code></pre></div> | <div><p>The exports are _copied_ at the time of requiring the module.<br>So even if an exported value changes in the module it was defined in, that change is **not** visible in the module where it's required.</p> For example: <pre><code>// somefile.js&#13;let count = 1;&#13;module.exports.count = count;&#13;&#13;setTimeout(() => count = 2, 1000);</code></pre> Now use it somewhere <pre><code>// main.js&#13;const mod = require('./somefile.js');&#13;&#13;console.log(mod.count); // 1&#13;setTimeout(() => console.log(mod.count), 1000); // 1</code></pre></div> |

If it's hard to read here, [read a copy of this table](https://github.com/akhanalcs/tour-of-heroes/blob/main/docs/learn-javascript.md#import-vs-require) in GitHub.

Resolved source:

<!-- language-all: javascript -->
|  | `import` (ES Modules syntax) | `require` (CommonJS Modules syntax) |
|:---:|:---|:---|
| **Summary** | <p>It is the latest standard for working with modules in JavaScript and is supported in modern browsers and environments that transpile or support ES6, like TypeScript or Babel.</p> | <p>It was not originally part of JavaScript, but was adopted as the standard for Node.js, which has been routinely used in JavaScript server-side development.<br><br>While Node.js historically used CommonJS, it now also supports ES6 modules.</p> |
| **Exports** | <p>Static (pre-defined). The structure of the module's exports is determined when the code is parsed, not while running.</p><p>This static nature allows tooling such as bundlers and linters to analyze the code without executing it, enabling features like better tree-shaking and faster load times in browsers.</p> For example: <pre><code>// myModule.js&#13;export function myFunc() { /\*...\*/ }&#13;export const MY_CONST = 123;</code></pre> | &#13;<p>Computed during runtime. The exports in a module are determined during the execution of the code.</p>For example: <pre><code>// myModule.js&#13;if (process.env.NODE_ENV === 'development') {&#13;  module.exports.debug = function debug() {&#13;    console.log('Debugging...');&#13;  }&#13;} else {&#13;  module.exports.log = function log() {&#13;    console.log('Logging...');&#13;  }&#13;}</code></pre> |
| **Loading Modules** | &#13;<p>Can be asynchronous, allowing efficient, selective loading of module parts. This can result in faster load times and better performance.</p>For example: <pre><code>import { myFunc } from './myModule.js';&#13;&#13;myFunc();</code></pre> | &#13;<p>Synchronous (loads modules one by one). Always loads entire module, which could affect performance if the module is large.</p>For example: <pre><code>const { debug, log } = require('./myModule.js');&#13;&#13;if(debug) debug();&#13;if(log) log();</code></pre> | 
| **Full Example** |  &#13;Make sure to export the function first. <pre><code>// somefile.js&#13;export function sayHello() {&#13;  console.log("Hello, world!");&#13;}&#13;&#13;console.log("somefile has been loaded!");</code></pre> Then import it <pre><code>// main.js&#13;import { sayHello } from './somefile.js';&#13;&#13;sayHello();&#13;&#13;// πŸ‘‡ Outputs πŸ‘‡&#13;// "somefile has been loaded!"&#13;// "Hello, world!"</code></pre> |  &#13;Make sure to add the function to `module.exports`. <pre><code>// somefile.js&#13;function sayHello() {&#13;  console.log("Hello, world!");&#13;}&#13;&#13;module.exports = { sayHello };&#13;&#13;console.log("somefile has been loaded!");</code></pre> Then import it <pre><code>// main.js&#13;const { sayHello } = require('./somefile.js');&#13;&#13;sayHello();&#13;&#13;// πŸ‘‡ Outputs πŸ‘‡&#13;// "somefile has been loaded!"&#13;// "Hello, world!"</code></pre> |
| **Scope** | &#13;<p>If an exported value changes in the module it was defined in, that change is visible in all modules that import this value.<p>For example: <pre><code>// somefile.js&#13;let count = 1;&#13;export { count };&#13;&#13;setTimeout(() => count = 2, 1000);</code></pre> Now use it somewhere <pre><code>// main.js&#13;import { count } from './somefile.js';&#13;&#13;console.log(count); // 1&#13;setTimeout(() => console.log(count), 1000); // 2</code></pre> | &#13;<p>The exports are _copied_ at the time of requiring the module.<br>So even if an exported value changes in the module it was defined in, that change is **not** visible in the module where it's required.</p> For example: <pre><code>// somefile.js&#13;let count = 1;&#13;module.exports.count = count;&#13;&#13;setTimeout(() => count = 2, 1000);</code></pre> Now use it somewhere <pre><code>// main.js&#13;const mod = require('./somefile.js');&#13;&#13;console.log(mod.count); // 1&#13;setTimeout(() => console.log(mod.count), 1000); // 1</code></pre> |

If it's hard to read here, [read a copy of this table](https://github.com/akhanalcs/tour-of-heroes/blob/main/docs/learn-javascript.md#import-vs-require) in GitHub.

Other observations caused by this answer:

  • When pressing the "Edit" button on the answer (and using the inline editor), all answers below this one on the page would turn into edit fields, any attempt to save edits would result in an error. Using the standalone editing page was required.
  • After editing the answer, pressing the Back browser button to return to the edit page would remove all markdown from the text field that occurred after the Summary row in the table.
3
  • 1
    So basically, the HTML sanitizer doesn't work inside tables... Commented Jan 27 at 1:16
  • @SonictheAnonymousHedgehog That does appear to be the issue, yep. Commented Jan 27 at 1:18
  • 1
    IMO the sanitizer should escape faulty or unsupported tags rather than trying to strip them. Commented Jan 27 at 22:49
9

As others have pointed out (thanks!), we had an issue with tag balancing during HTML validation. It's been fixed and the fix is out in production now.

Thanks for reporting this, and please let me know if you see any other related issues.

4
  • can you please push the fix to the SEDE github repo?
    – starball
    Commented Feb 7 at 20:14
  • 2
    @starball Didn't end up modifying the balancer itself, so there's nothing to push. The core of the issue was with how we generated HTML markup for tables, so I fixed that instead.
    – Adam Lear StaffMod
    Commented Feb 7 at 21:09
  • 1
    @AdamLear Why was the </div> not getting sanitized out of the post? I thought the HTML sanitizer would remove it from the Markdown before it goes through the renderer. Commented Feb 7 at 22:55
  • @SonictheAnonymousHedgehog It did! Kind of. :) We don't normally allow divs, but our tables are wrapped in a <div> to make them scrollable at smaller screen sizes, so an exception was made in that scenario. Then things got weird - opening divs that were in the table got removed, and the first closing one got matched up with the div that wraps the table. Then the rest of the closing divs were removed as well and tada, a broken page. We changed how we generate table markup from markdown, so going forward we are removing all divs as intended.
    – Adam Lear StaffMod
    Commented Feb 8 at 22:33
6

Part of why this happens is the tag balancer's basic implementation (the other part is presumably the pipetables extension for markdig). I would say "naive" instead of "basic", but I'm not the designer, so maybe it's like that as a performance-vs-correctness tradeoff. And the rest is up to your user agent's tag soup parser.

FYI, this can be minimally reproduced with the following Markdown:

|</div>
|-|

, yielding

<div class="s-table-container">
<table class="s-table">
<thead>
<tr>
<th></div></th>
</tr>
</thead>
</table>

The following also works:

||
|-|
|</div>

, yielding

<div class="s-table-container">
<table class="s-table">
<thead>
<tr>
<th></th>
</tr>
</thead>
<tbody>
<tr>
<td></div></td>
</tr>
</tbody>
</table>
3
  • 1
    I tried this out in the Sandbox - and I found that I could not edit the post afterwards (any attempt, regardless of the change, would give me a generic error message). Even deleting it doesn't restore the page, at least for me (presumably because I can see the deleted post, as it's mine). Also, post deletion, the pink background CSS is also applied to every other answer. (I hoped to verify the exact requirements. My hypothesis is that the sanitizer is removing the opening tag, but leaving the closing tag.) Commented Jan 27 at 23:00
  • 2
    @KarlKnechtel I covered this in my answer, trying to edit mangles the page when using the inline editor, middle/ctrl click the edit button to open the stand-alone editor page, you should be able to edit from there. Commented Jan 28 at 4:38
  • @KarlKnechtel weird enough, if going directly to the edit page (middle click on the "edit" link is the easiest way) it does work, I've removed the code and now it doesn't break the page. (Still broken in the revisions but that's expected.) Commented Jan 29 at 13:03

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .