updating an equation using innerHTML

2007-07-30
2013-04-29
  • The code below doesn't work - x^5 is displayed OK but clicking either button causes all the jsMath formatting to disappear.

    The call to jsMath.ConvertTeX() clearly does something as the $s around the expression are removed, but that's it.

    Any ideas?

    thanks,

    Mike

    <SCRIPT SRC="jsMath/easy/load.js"></SCRIPT>

    <p id="message1">$x^2+3x-1$</p><br/>
    <p id="question">$x^7$</p><br/>

    <input type="button" value="click me1" onclick="click1()">
    <input type="button" value="click me2" onclick="click2()">

    <script>
    Q1=document.getElementById('question');
    Q1.innerHTML="$x^5$";

    function click2() {
    Q1=document.getElementById('question');
    Q1.innerHTML="$x^2$";
    }

    function click1() {
    Q1=document.getElementById('question');
    Q1.innerHTML="$x^3$";
    jsMath.ConvertTeX();
    }
    </script>

     
    • To answer my own question, this works:

      <SCRIPT SRC="jsMath/jsMath.js"></SCRIPT>

      <DIV CLASS="math" id="maths">
      </DIV>

      <input type="button" value="click me1" onclick="click1()">
      <input type="button" value="click me2" onclick="click2()">

      <script>
      Q1=document.getElementById('maths');
      Q1.innerHTML = '\\sum_{i=1}^{n} i = {n(n+1)\\over 2}';
      jsMath.Translate.ProcessElement(Q1);

      function click2() {
      Q1.innerHTML = 'x \\times y';
      jsMath.Translate.ProcessElement(Q1);
      }

      function click1() {
      Q1.innerHTML = '\\sum_{i=1}^{n} i = {n(n+1)\\over 2}';
      jsMath.Translate.ProcessElement(Q1);
      }
      </script>

       
    • simon1tan
      simon1tan
      2007-11-30

      You're second post doesn't work!  I used the Parse(equation) found in http://www.math.union.edu/~dpvc/jsmath/jsMath-lab.html but would like a better way of doing it.

       
      • Definitely works for me on Firefox and IE6/7.

        Post a link to a webserver with it on and I'll have a look.

        (NB Internet Explorer doesn't seem to work with jsMath on local pages, so if this is the problem try Firefox or upload to a server)

         
        • Version 3.4e fixed the problem with loading local files in MSIE.

           
    • simon1tan
      simon1tan
      2007-11-30

      Wait, how come it doesn't work on local pages??  Same problem with FireFox 2. The stuff I'm working at home works fine(I'm not using jsMath.Translate.ProcessElement() though but I would like to :))  Like I said in previous post, I'm using Parse() but replace <span> with <div> so I get what amounts to LineFeeds. I was trying to put multiple equations sort of like content.innerHTML += content.innerHTML + newEquation to simulate "show your work," as my math instructors would always want. It's working now. I still don't understand much of the jsMath source, it's all over the place.

      I copied your code, pasted into text file(renamed to html), fixed <script src=jsMath.js</script> to point to my jsMath location and clicked on the 2 buttons.  They both output the tex code instead of the equation.  The wierd thing is, the initial equation displays fine, just the buttons don't output right. After clicking a button, I double click on the incorrect output and a little popup shows the old equation. That might be a clue.  You're using version 3.4f right?

      I'm wondering if this is a bug?  I put <body onclick="alert(this.innerHTML)"> to display the output after I click the button.  Below is what is returned(some junk deleted, still hard to read). Notice it returns a <div class=typeset> and another <div class=math>. Something's not right.

      <DIV class=typeset id=maths style="BORDER-RIGHT: black 1px solid; BORDER-TOP: black 1px solid; BORDER-LEFT: black 1px solid; WIDTH: 500px; BORDER-BOTTOM: black 1px solid" alt="\sum_{i=1}^{n} i = {n(n+1)\over 2} ">

      <DIV class=math>x \times y</DIV></DIV>

      OK, I think I've figured it out.  It changes <div class=typeset id=maths>. The next time you use Q1.innerHTML, it's not class=math because it been changed to class=typeset!!  Don't know where in jsMath this takes place.

      Yep, just changed your function to:
      function click2() {
      Q1=document.getElementById('maths');
      Q1.innerHTML = '\\sum_{i=1}^{n} i = {n(n+1)\\over 2}';
      Q1.className = "math"
      jsMath.Translate.ProcessElement(Q1);
      }

      and now it's working.

       
      • Ah, I'm using 3.4c (didn't notice a newer version :)

        I confirm what you see, the line Q1.className = "math" makes my file work on 3.4d, 3.4e, 3.4f

        Thanks for the fix - good skills on the debugging: alert(this.innerHTML) is very cunning :)

         
    • I'm glad to see that you have worked out the difficulties.  One of the changes in v3.4d as to have ProcessElement() only process elements marked as class="math"  in order to prevent jsMath from trying to retypeset an element that was already typeset previously.  Once jsMath processes a TeX expression, its class is changed to class="typeset".  This allows you to use CSS to style the un-typeset mathematics differently from the typeset mathematics (e.g., the un-typeset math could be in a different color, or hidden, or some other indication that it is math that is yet to be typeset).

      The jsMath.Translate.ProcessElement() call is not really meant to be called by users (it is an internal routine used by jsMath), so there are some issues you should know about.  For example, if the mathematics that is being typeset requires an extension or font to be loaded, then you need to take extra steps to assure that the math will really be typeset correctly.  Also, ProcessElement() is not automatically synchronized with jsMath's command stack, so you run the risk of it running out of sequence (e.g., before some component that it requires has been loaded).  This probably will only show up if an extension must be loaded, but it will also be a problem if you were to use jsMath/easy/load.js rather than loading jsMath.js directly.  (This is because easy/load.js doesn't load jsMath until the window's onload handler runs, so, for example, your first call to jsMath.Translate.ProcessElement() will try to run BEFORE jsMath has been loaded, and so will fail).

      For these reasons, I suggest that you stick with the standard calls for processing mathematics, jsMath.Process() or jsMath.ProcessBeforeShowing().  The latter should work for your purposes.  These both are synchronized with jsMath automatically, and both handle the situation where an extension must be loaded in order to process the mathematics.

      Here is one solution to your problem:

          <SCRIPT SRC="jsMath/jsMath.js"></SCRIPT>

          <DIV id="maths"></DIV>

          <input type="button" value="click me1" onclick="click1()">
          <input type="button" value="click me2" onclick="click2()">

          <SCRIPT>
          function DisplayMath(element,math) {
            if (typeof(element) == 'string') {element = document.getElementById(element)}
            element.innerHTML = '<DIV CLASS="math">'+math+'</DIV>';
            jsMath.ProcessBeforeShowing(element);
          }

          Q1=document.getElementById('maths');
          DisplayMath(Q1,'\\sum_{i=1}^{n} i = {n(n+1)\\over 2}');

          function click1() {DisplayMath(Q1,'\\sum_{i=1}^{n} i = {n(n+1)\\over 2}')}
          function click2() {DisplayMath(Q1,'x \\times y')}
          </SCRIPT>

      Here, I've provided a function that handles the details of inserting the mathematics for a particular element and calling jsMath to process it.  That means your click functions only need to specify the element and the new mathematics for the element.  The element could be given as its ID string or as a reference to the element itself (as is done here).  The DisplayMath() function wraps the math in a DIV of class="math" and asks jsMath to process it.  The jsMath.ProcessBeforeShowing() call takes care of all the details I mentioned above.

      Note that in the case where an extension or font is loaded during the processing of the mathematics, the jsMath.ProcessBeforeShowing() call may return BEFORE the mathematics is fully typeset, so it may be a while before the contents of the Q1 element actually is the typeset mathematics in that case.  If you need to query the element after the math is typeset (e.g. to find out its size or something) then you would need to use jsMath.Synchronize() to handle  that.  If you are sure that no extensions or fonts need to be loaded, then what you have done should work, but I think you are safer with the jsMath.ProcessBeforeShowing() call.

      Davide

       
    • You mention wanting to do a "show your work" layout as the motivation for your wanting to modify the equation being displayed by jsMath.  There are alternatives that might prove more efficient for you.  You could, for example, typeset all the mathematics initially, but use CSS to hide portions of it and then have your button reveal the parts a line at a time.  This could be done in several ways:  one would be to use a table where each row was a separate step in the process, and have all but the first row with STYLE="visibility:hidden" (or even STYLE="display:none"), and then have the button change the visibility to "visible" for successive rows as it is pressed.  For example (provided jsMath/easy/load.js has been configured to process dollar signs):

      <SCRIPT SRC="jsMath/easy/load.js"></SCRIPT>

      <CENTER>
      <TABLE BORDER="0" CELLSPACING="0" CELLPADDING="0">
      <TR><TD>$p(x)$</TD><TD>${}={}$</TD><TD>$(x+2)^2$</TD></TR>
      <TR ID="R2" STYLE="visibility:hidden"><TD></TD><TD>${}={}$</TD><TD>$(x+2)(x+2)$</TD></TR>
      <TR ID="R3" STYLE="visibility:hidden"><TD></TD><TD>${}={}$</TD><TD>$x^2+2x+2x+2^2$</TD></TR>
      <TR ID="R4" STYLE="visibility:hidden"><TD></TD><TD>${}={}$</TD><TD>$x^2+4x+4$</TD></TR>
      </TABLE>
      </CENTER>

      <input type="button" value="Show Work" id="Show Work" onclick="revealRow()">

      <script>
      var ShowingR = 1; var MaxR = 4;
      function revealRow() {
        ShowingR++;
        var row = document.getElementById('R'+ShowingR);
        row.style.visibility="visible";
        if (ShowingR == MaxR) {document.getElementById("Show Work").disabled = 1}
      }
      </script>

      This creates a table with four rows, the last three not showing.  Pressing the "Show Work" button will reveal additional rows until they are all showing.

      Alternatively, you could use \eqalign to lay out the complete expression you want, and use jsMath's \cssId and \style extensions to mark some rows as not showing, then have the button reveal them one at a time.  For example:

      <SCRIPT SRC="jsMath/easy/load.js"></SCRIPT>

      <DIV CLASS="math">
      \eqalign{
        p(x) &= (x+2)^2\cr
             &\cssId{R2}{\style{visibility:hidden}{= (x+2)(x+2)}}\cr
             &\cssId{R3}{\style{visibility:hidden}{= x^2+2x+2x+2^2}}\cr
             &\cssId{R4}{\style{visibility:hidden}{= x^2+4x+4}}\cr
      }
      </DIV>

      <input type="button" value="Show Work" id="Show Work" onclick="revealRow()">

      <script>
      var ShowingR = 1; var MaxR = 4;
      function revealRow() {
        ShowingR++;
        var row = document.getElementById('R'+ShowingR).firstChild;
        row.style.visibility="visible";
        if (ShowingR == MaxR) {document.getElementById("Show Work").disabled = 1}
      }
      </script>

      Here, the \cssId macro is used to identify the rows, and the \style macro is used to mark them as hidden.  This creates a SPAN with the given ID containing a SPAN that has its visibility set to hidden.  The revelaRow() function looks up the specified row, but since it is the SPAN inside it that is marked as hidden, we use the firstChild field to get to it from the parent SPAN with the given ID.  The rest is essentially the same as above.

      These may work better than creating mathematics on the fly.  In any case, they are alternative approaches.

      Davide