0

I'm using ajax to load all the content from 'mysite/math/' into math.php. Inside math.php I want to render the loaded math content using katex.

https://github.com/Khan/KaTeX

Inside math.php I load the library katex from the cdn which can be found in the link above.

math.php's html:

<body>
  <div id='main'>
</body>

Inside math.php's script tags, i have some php to get a list of urls from 'mysite/math/'

echo "var x = [];";
$dir = "./math/";
$a = scandir($dir);
foreach ($a as $x) {
if ($x === '.' or $x === '..') continue;
  echo "x.push('mysite/math/" . $x . "');";
}

So this gives me an array, x, which contains the location of each file whose content I want to load into my webpage.

Now, in javascript, I make multiple AJAX calls to the urls in the array x:

// defining the ajaxing function
function myfunction(url, someFunction) {
  var xhr = new XMLHttpRequest();
  xhr.onreadystatechange = function () {
    if (this.readyState === 4 && this.status === 200) {
      someFunction(this, url);
    }
  };
  xhr.open('GET', url, true);
  xhr.send(null);
}

// defining the callback function
function callbackfunction(xhr, url) {
  var name = url;
  var div = document.createElement('div');
  div.innerHTML = xhr.responseText;
  div.className += name;
  document.getElementById('main').appendChild(div);
}

// here, I'm actually executing the ajax calls
for (var i = 0; i < x.length; i++) {
  myfunction(x[i] + '?w=' + Math.random(), callbackfunction);
}

All of this works fine so far.

The problem:

Inside each of the html files living inside 'mysite/math/' there are span tags with the class='math' that contain the math content I want to render. So inside math.php, I see all my content, and I see my span tags with class='math'; they're all there.

Inside math.php there is also the following javascript which uses the katex function katex.render()

var math = document.getElementsByClassName('math');
for (var i = 0; i < math.length; i++) {
  katex.render(math[i].innerHTML, math[i]);
}

The way I've used katex works fine if the content is not loaded using ajax.

Note: I've seen this question asked several times but the answers are always using jquery. I would like a solution in javascript.

dactyrafficle
  • 838
  • 8
  • 18

2 Answers2

1

The problem is very likely that your AJAX-supplied content is not yet on the page when the browser invokes katex.render.

Since you're making multiple calls to load your content, you need to wait until all of the calls have returned to invoke katex.render. There are a couple of ways I can think of to do this using vanilla JavaScript:

  1. Create an event handler that listens for each of the calls to finish. When they're all done (maybe keep track of the number of calls completed in a variable,) invoke katex.render.
  2. Wrap each of your AJAX calls in a promise, push them all into an array, and invoke Promise.all on the array. In Promise.all().then(), invoke katex.render.
Matt Morgan
  • 4,900
  • 4
  • 21
  • 30
  • I thought of the first option you listed: creating an event handler. I'm just having some trouble thinking one up. Do you have any suggestions? As for the second, I don't know what a promise is so I will probably looking int it. – dactyrafficle Apr 02 '18 at 02:16
  • Personally I would lean toward (2)-- Promises. It's a very useful thing to know generally if you're writing JavaScript. Here's a nice explanation of how to turn a native XHR call into a Promise: https://stackoverflow.com/questions/30008114/how-do-i-promisify-native-xhr – Matt Morgan Apr 02 '18 at 14:44
  • Here's MDN's explanation of Promise.all(): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all – Matt Morgan Apr 02 '18 at 14:45
  • My js skill still needs a lot of work so i'll keep looking into promise objects. But, I do think I've found a way to get the result I wanted. I'm not sure if it's the elegant way to go, but I am now applying katex.render to each div's children as the div is added to the page seems to work. – dactyrafficle Apr 07 '18 at 18:57
0

This solution is not so sophisticated but I think it does the job.

I changed my callback function by including a line applyKatex(div)

// defining the callback function
function callbackfunction(xhr, url) {
  var name = url;
  var div = document.createElement('div');
  div.innerHTML = xhr.responseText;
  div.className += name;
  document.getElementById('main').appendChild(div);
  applyKatex(div); //  <---this is what i added
}

I made a function, applyKatex() which accepts an html element and applies the function katex.render() to each child-element with the class name 'math'

As each new div is added to the page, katex.render() will be applied to it

function applyKatex(element) {
  var math = element.getElementsByClassName('math');
  for (var i = 0; i < math.length; i++) {
    katex.render(math[i].innerHTML, math[i]);
  }
}
dactyrafficle
  • 838
  • 8
  • 18