multiple, dynamically generated textboxes -> MathJax

389 views
Skip to first unread message

Grossman, Robert B

unread,
Oct 4, 2012, 11:08:40 PM10/4/12
to mathja...@googlegroups.com
Hi,

I have created a page, http://epoch.uky.edu:9135/ace/public/enterEqns.jsp, where a user can enter LaTeX into a textbox, then add another textbox, add more LaTeX, then add another textbox, etc.

I use innerHTL to rewrite all the textboxes and their contents each time a new equation is added.  (This approach allows me, for instance, to allow the user to insert or delete an equation in the middle of the list, although the functions to do that aren't currently available.)

The page first sets up a single textbox.  After it write the necessary HTML to the page, it calls MathJax.Hub.Configured().  At this point, if I enter LaTeX into the textbox, MathJax formats it correctly.

However, if I add a new textbox, the formatted text goes away, and now typing text into any textbox fails to generate any formatted math.

Furthermore, if I now delete that textbox that I just added, there is still no formatting going on.

I used the code in sample-dynamic.html that shipped with MathJax as a model, but at this point I don't know what else to do.  BTW, I'n not familiar at all with JQuery, and this may be hindering my ability to solve this problem.

Any help anyone can offer would be greatly appreciated.

-- Bob Grossman

Grossman, Robert B

unread,
Oct 5, 2012, 3:44:43 PM10/5/12
to mathja...@googlegroups.com
Hi,

I have created a page, http://epoch.uky.edu:9135/ace/public/enterEqns.jsp, where a user can enter LaTeX into a textbox, then add another textbox, add more LaTeX, then add another textbox, etc.   Each textbox, whose id is eqn1, eqn2, etc., has a corresponding mirror field, mirror1, mirror2, etc.  As a user types text into a particular textbox, I want the formatted equation to appear in the corresponding mirror field.  

After a lot of fiddling around, I found that all of the equations will be formatted properly if I reload the whole page after I add or remove a textbox.  Awkward, but it works.

The problem remains, though, that no matter which textbox I enter text into, the change is always displayed in the field mirror1, associated with textbox1. 

Here's the MathJax code I'm currently using:

    MathJax.Hub.Config({
        tex2jax: {
            inlineMath: [["$","$"],["\\(","\\)"]]
        }
    });         
        
    (function () {
        var QUEUE = MathJax.Hub.queue; // shorthand for the queue
        var math = null; // contains the element jaxes for the math output
        var eqnNum = 0;
    
        // Get the element jax when MathJax has produced it.
        QUEUE.Push(function () {
            var mirrorName = 'mirror' + eqnNum;
            hideCell(mirrorName);
            math = MathJax.Hub.getAllJax(mirrorName)[0];
            showCell(mirrorName);
        });
    
        // The onchange event handler that typesets the math entered
        // by the user.  Hide the box, then typeset, then show it again
        // so we don't see a flash as the math is cleared and replaced.
        window.UpdateMath = function (eqnNumber) {
            eqnNum = eqnNumber;
            QUEUE.Push(['Text', math,
                        '\\displaystyle{' + getValue('eqn' + eqnNum) + '}']);
        }
    })();

I tried changing the math variable into an array so that MathJax would recognize the existence of more than one mirror field, but I just got an error message;

    (function () {
        var QUEUE = MathJax.Hub.queue; // shorthand for the queue
        var math = new Array(); // contains the element jaxes for the math output
        var eqnNum = 0;
    
        // Get the element jax when MathJax has produced it.
        QUEUE.Push(function () {
            var mirrorName = 'mirror' + eqnNum;
            hideCell(mirrorName);
            math[eqnNum] = MathJax.Hub.getAllJax(mirrorName)[0];
            showCell(mirrorName);
        });
    
        // The onchange event handler that typesets the math entered
        // by the user.  Hide the box, then typeset, then show it again
        // so we don't see a flash as the math is cleared and replaced.
        window.UpdateMath = function (eqnNumber) {
            eqnNum = eqnNumber;
            QUEUE.Push(['Text', math,
                        '\\displaystyle{' + getValue('eqn' + eqnNum) + '}']);
        }
    })();

The error message: Error: Can't make callback from given data.

(BTW, the error message originates at line 29 of MathJax.js, but all of the code in MathJax.js is on a single line, so the error message is unhelpful.  Don't you guys believe in return characters?)

Any help anyone can offer would be greatly appreciated.  

I would especially like it if I could dispense with reloading the page whenever I add or remove textboxes.  The reason I am forced to reload the page after I add or remove textboxes is that I rewrite the entire table.  I think that MathJax stores references to the mirror fields, and when I rewrite the entire table, MathJax can no longer find the fields to which it has references.  So I need to destroy the whole MathJax object and start over each time I add or remove a textbox, and that means that I need to reload the whole page.  It would be great if I could instead reinitiate the MathJax object without reloading the whole page.

-- Bob Grossman

Davide P. Cervone

unread,
Oct 6, 2012, 4:11:38 PM10/6/12
to mathja...@googlegroups.com
There are a number of problems with your approach.  Note that MathJax.Hub.Configured() is only for when you are delaying configuration of MathJax until after the MathJax.js file has been loaded.  And in that case, MathJax puts off its configuration until you make this call, and since the initial typesetting run has to wait until after configuration is complete, the initial typesetting is also put off.  So your call to MathJax.Hub.Configured() would cause the typesetting to occur, but only the first time you call it.

Using innerHTML to replace all the textboxes and equations is also a problem.  You could make it work using the techniques in the http://cdn.mathjax.org/mathjax/latest/test/sample-dynamic-2.html file instead.  You would have to queue a call to MathJax.Hub.Typeset() rather than a the Text() of a single math element jax, since you want to typeset a whole collection of expressions.  

But doing so is inefficient since you will re-typesetting many equations that don't need it.  (If instead you are using the old value of innerHTML and adding to it, then although that will keep the old typeset equations, it will lose the connection to the contextual menu and other actions that MathJax could perform, so its still a bad idea.)  It is better simply to insert the new text box into the existing DOM and update the single math element associated with it when its value changes.

The following is an example of how to do this.

<!DOCTYPE html>
<html>
<head>
<title>Dynamic Equation List</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<script type="text/x-mathjax-config">
MathJax.Hub.Config({
  tex2jax: { inlineMath: [['$','$']] }
});
</script>
<script src="../MathJax/MathJax.js?config=TeX-AMS_HTML"></script>
<script>
var Equation = {
  //
  // onclick and onchange routines for buttons and type-in areas
  // (avoids creating new closures for each button)
  //
  addEqn: function () {Equation.Add(this)},
  removeEqn: function () {Equation.Remove(this)},
  updateEqn: function () {Equation.Update(this)},
  //
  //  Add a new equation prior to the one where the plus button was pressed
  //    Create a new equation DIV with +, -, tex, and typeset areas
  //    Insert it into the list of equations at the right spot
  //    Typeset it and show the results (the math is initially hidden to avoid flicker)
  //
  Add: function (input) {
    var div = input.parentNode;
    var eqn = MathJax.HTML.Element("div",{},[
      ["input",{type:"button",value:"+",onclick:this.addEqn}],
      ["input",{type:"button",value:"-",onclick:this.removeEqn}]," ",
      ["input",{type:"text",size:"40",onchange:this.updateEqn,style:{"margin-right":"5em"}}],
      ["span",{style:{visibility:"hidden"}},["${}$"]]
    ]);
    div.parentNode.insertBefore(eqn,div);
    MathJax.Hub.Queue(
      ["Typeset",MathJax.Hub,eqn],
      ["Show",this,eqn]
    );
  },
  //
  //  Remove the equation and its buttons and typset form
  //
  Remove: function (input) {
    var eqn = input.parentNode;
    eqn.parentNode.removeChild(eqn);
  },
  //
  //  Get the element jax for the associated equation,
  //    hide the math, set its text and typeset it, then show it again
  //
  Update: function (input) {
    var eqn = input.parentNode;
    var math = MathJax.Hub.getAllJax(eqn)[0];
    MathJax.Hub.Queue(
      ["Hide",this,eqn],
      ["Text",math,input.value],
      ["Show",this,eqn]
    );
  },
  //
  //  Hide and show math (during typesetting, so you don't see the initial TeX code)
  //
  Hide: function (eqn) {eqn.lastChild.style.visibility = "hidden"},
  Show: function (eqn) {eqn.lastChild.style.visibility = ""}
}
</script>
</head>
<body>
<div id="eqn_list">
<div><input type="button" value="+" onclick="Equation.Add(this)" id="last" /></div>
</div>
<script>
Equation.Add(document.getElementById("last"));  // Create initial equation
</script>
</body>
</html>

See if that helps you out.

Davide

Davide P. Cervone

unread,
Oct 6, 2012, 4:14:46 PM10/6/12
to mathja...@googlegroups.com
PS, as I recall, IE doesn't trigger onchange events until you move out of the textbox, so this might not work quite as expected in IE.  This is just a sample to show the logic behind handling the lists of equations.

Davide

Davide P. Cervone

unread,
Oct 6, 2012, 4:24:09 PM10/6/12
to mathja...@googlegroups.com
After a lot of fiddling around, I found that all of the equations will be formatted properly if I reload the whole page after I add or remove a textbox.  Awkward, but it works.

I think you will find my previous example to be a more efficient approach.

I tried changing the math variable into an array so that MathJax would recognize the existence of more than one mirror field, but I just got an error message;

The QUEUE.Push(['Text',math,...]) call means to queue a call for math.Text().  If math is an array, you are trying to call the Text method of an Array object, but there is no such method, so you get the error.

(BTW, the error message originates at line 29 of MathJax.js, but all of the code in MathJax.js is on a single line, so the error message is unhelpful.  Don't you guys believe in return characters?)

You are using the compressed version of MathJax, and the compressor removes (among other things) the linebreaks.  If you load http://cdn.mathjax.org/mathjax/latest/unpacked/MathJax.js rather than http://cdn.mathjax.org/mathjax/latest/MathJax.js you will be using the human readable form with comments, line-breaks, and all.

I would especially like it if I could dispense with reloading the page whenever I add or remove textboxes.  The reason I am forced to reload the page after I add or remove textboxes is that I rewrite the entire table.  I think that MathJax stores references to the mirror fields, and when I rewrite the entire table, MathJax can no longer find the fields to which it has references.  So I need to destroy the whole MathJax object and start over each time I add or remove a textbox, and that means that I need to reload the whole page.  It would be great if I could instead reinitiate the MathJax object without reloading the whole page.

Your analysis is incorrect about the source of the problem.  MathJax doesn't store any references to the DOM objects.  It is that you are not using the correct calls to tell MathJax to retypeset the changed elements.

You might want to read the documentation page on modifying math on the page


for details.

Davide
Reply all
Reply to author
Forward
0 new messages