Removed jQuery Dependency from My Math Editor

Background

jQuery provides many handy functions to speed up the development, for example document.ready(), element.toggle(), element.hide(), etc. My math editor used some of them. However, I replaced them with Vanilla JavaScript at commit 4a35c45b.

Goal

Some users complained about the white background in the past versions of my editor. That had motivated me to refactor the code, so that it would be easier to maintain and to add in new features.

HTML5 input types used

Actual appearance might vary across browsers.

  1. <input type="color" id="bgcolor" value="#424549"> for selecting color.
  2. <input type="range" min="9" max="24" value="20" class="slider" id="fontSize"> for sliders.

JavaScript skills used

Replace $("{cssSelector}") with document.querySelector("{cssSelector}").

Replace getter and setters .text(), .value() and .html() with .innerText, .value and .innerHTML respectively.

Replace jQuery event functions like elt.click() with event listeners elt.addEventListener("click", (event) => {...}).

Replce jQuery’s elt.css("css-prop", val) with elt.style.cssProp = val. The cssProp in camelCase doesn’t has to be quoted, like elt.style.width = "80%".

Document loaded

We add a listener to the document (or other target tag) for DOMContentLoaded event with a(n anonymous) handler, which is a function.

document.addEventListener("DOMContentLoaded", function () {
  renderMathInElement(preview, myKaTeXOptions);
});

Button click detection

Instead of jQuery’s button.click(handler), we listen to the click event.

/* print button */
const prBtn = .querySelector("#prBtn");
prBtn.addEventListener("click", (event) => {
  window.print();
  return false;
});

Get input from input fields and selection lists

To access the element on the left of .addEventListener, use event.target. Here’s one example usage in my script. The folloiwng HTML code gives a <select>ion list. The default <option> has selected value.

<select id="fontList" name="font-family">
  <option value="Arial">Arial</option>
  <option selected value="Times New Roman">Times New Roman</option>
  <option value="Courier New">Courier New</option>
  <!-- other fonts omitted -->
</select>

The same query selector [attr="val"] also works for CSS.

/* font  */
const ffList = document.querySelector('select[name="font-family"]');
ffList.addEventListener("input", (event) => {
  previewTag.style.fontFamily = event.target.value;
});

For changing colors, I have to change the CSS property color for document.body, input text area srcTag and font selection list ffList separately. I’ve replaced repeated .css('color', fs) with a forEach loop.

/* foreground color */
const fgColorBtn = document.querySelector('#fgcolor');
fgColorBtn.addEventListener("input", (event) => {
  [document.body, srcTag, ffList].forEach(tag => {
    tag.style.color = event.target.value;
  });
});

Text area input detection

Vicky Chijwani has provided the following code block for text area input detection in his answer on text area input event.

var area = container.querySelector('textarea');
if (area.addEventListener) {
  area.addEventListener('input', function() {
    // event handling code for sane browsers
  }, false);
} else if (area.attachEvent) {
  area.attachEvent('onpropertychange', function() {
    // IE-specific event handling code
  });
}

Some claimed that pasting won’t trigger the input event. That’s not what I have observed from W3 Schools oninput demo.

I’ve loaded the source and target tags as constants, and used a custom function taHandler to handle the input.

const srcTag = document.querySelector("#userInput");
const previewTag = document.querySelector("#preview");

// detect textarea event
if (srcTag.addEventListener) {
  srcTag.addEventListener("input", taHandler, false);
} else if (srcTag.attachEvent) {
  srcTag.attachEvent("onpropertychange", taHandler);
}

let oldVal = srcTag.value;
function taHandler(event) {
  let currentVal = .target.value;
  if (currentVal === oldVal) return;
  oldVal = currentVal;
  previewTag.innerHTML = converter.makeHtml(currentVal);
}

The converter comes from Showdown. Since it’s also a constant, it should be declared at the top.

Async clipboard API

Thanks to Dean Taylor’s “async + fallback” Javascript clipboard solution, I’ve wrapped the origninal anonymous handler inside cpFallBack() because document.execCommand("copy") is deprecated.

/* copy button */
const cpBtn = document.querySelector("#cpBtn");
cpBtn.addEventListener("click", (event) => {
  copyTextToClipboard(srcTag.value);
});

function cpFallBack(){
  try {
    srcTag.select();
    document.execCommand("copy");
  } catch (err) {
    console.error('Fallback: Oops, unable to copy', err);
  }
}

function copyTextToClipboard(text) {
  if (!navigator.clipboard) {
    cpFallBack();
    return;
  }
  navigator.clipboard.writeText(text).then(() => {}, () => {});
}

The .then(success, fail) method accepts two functions success and fail.


No comment

Your email address will not be published. Required fields are marked *.