skip to Main Content

The <math> element works as expected when written by hand, but doesn’t render properly when created with js dynamically:

let math = document.createElement('math');
math.setAttribute('display', 'block');

let mfrac = document.createElement('mfrac');
let ms1 = document.createElement('ms');
let ms2 = document.createElement('ms');

ms1.textContent = 'this';
ms2.textContent = 'doesn't';

mfrac.appendChild(ms1);
mfrac.appendChild(ms2);

math.appendChild(mfrac);

document.body.appendChild(math);
<!DOCTYPE html>
<html>

<body>
  <math display="block">
        <mfrac>
            <ms>this</ms>
            <ms>works</ms>
        </mfrac>
    </math>
</body>

</html>

Screenshot of the fraction not rendering:

enter image description here

Updating the existing <math> elements by inserting new children doesn’t change their appearance at all, despite the document changing in the inspector.

2

Answers


  1. math and its associated elements is one of the two "it’s now in the HTML spec, but JS still only lets you build them as namespaced XML elements" (the other being svg), so you’ll need to use createElementNS with the http://www.w3.org/1998/Math/MathML namespace. It doesn’t count as a "real" math element if you don’t.

    const MathNS = `http://www.w3.org/1998/Math/MathML`;
    const createMathElement = (tag) => document.createElementNS(MathNS, tag);
    
    const math = createMathElement(`math`);
    math.setAttribute(`display`, `block`);
    
    let mfrac = createMathElement(`mfrac`);
    let ms1 = createMathElement(`ms`);
    let ms2 = createMathElement(`ms`);
    
    ms1.textContent = `this`;
    ms2.textContent = `also works`;
    
    mfrac.appendChild(ms1);
    mfrac.appendChild(ms2);
    math.appendChild(mfrac);
    
    document.body.appendChild(math);
    <!DOCTYPE html>
    <html>
    
    <body>
      <math display="block">
            <mfrac>
                <ms>this</ms>
                <ms>works</ms>
            </mfrac>
        </math>
    </body>
    
    </html>
    Login or Signup to reply.
  2. As @MikePomaxKamermans suggested, here it is as a "separate answer":

    document.body.insertAdjacentHTML("beforeend",`<math display="block"><mfrac><ms>and this</ms><ms>works too!</ms></mfrac></math>`)
    
    
    const MathNS = `http://www.w3.org/1998/Math/MathML`;
    const createMathElement = (tag) => document.createElementNS(MathNS, tag);
    
    const math = createMathElement(`math`);
    math.setAttribute(`display`, `block`);
    
    let mfrac = createMathElement(`mfrac`);
    let ms1 = createMathElement(`ms`);
    let ms2 = createMathElement(`ms`);
    
    ms1.textContent = `this`;
    ms2.textContent = `also works`;
    
    mfrac.appendChild(ms1);
    mfrac.appendChild(ms2);
    math.appendChild(mfrac);
    
    document.body.appendChild(math);
    
    document.body.insertAdjacentHTML("beforeend",`<math display="block"><mfrac><ms>and this</ms><ms>works too!</ms></mfrac></math>`)
    <!DOCTYPE html>
    <html>
    
    <body>
      <math display="block">
            <mfrac>
                <ms>this</ms>
                <ms>works</ms>
            </mfrac>
        </math>
    </body>
    
    </html>

    As Mike already explained, the above approach is "exploiting the DOM parser’s ability to tell which elements need which namespaces automatically."

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search