如果我們是含有數學符號的靜態網頁,那只要依照第十天的方法,就可以輕鬆處理;然而,如果我們的運算式是由使用者輸入得到,也就是動態顯示數學符號,那程序就會麻煩多了。
如果要動態呈顯文字輸入框,一般會使用MathJax.typeset()搭配math.js幫我們解析運算式;若要從後端傳送數學方程式符號,則最好應用MathJax.typesetPromise()的非同步處理。
載入math.js
<script type="text/javascript" src="https://unpkg.com/mathjs@9.5.1/lib/browser/math.js"></script>
首先我們需要math.js解析我們輸入的運算式並計算其值,Math.js處理運算式可拆分為parse、compile和evaluate三個步驟。
Math.js運算式的語法大致上和Javascript的運算式語法,最明顯的差異是提供了指數運算符號(^),另外則提供了複數運算和矩陣運算,以下是一些運算式的例子
// expressions
math.evaluate('12 / (2.3 + 0.7)') // 4
math.evaluate('12.7 cm to inch') // 5 inch
math.evaluate('cos(2*pi/3)') // -0.4999999999999998,正確答案應是-0.5, 有誤差
math.evaluate('13 / (3 + 2i)').toString() // 複數運算3 - 2i
math.evaluate('det([-1, 2; 3, 1])') // 計算行列式值-7
math.evaluate('2a+3b', {a: 3, b: 5}) // 21
當運算式內含有變數時,evaluate()可以有一個選擇性的物件參數scope,用其設定變數的值。
我們除了計算運算式的值外,最重要的是parse運算式之後,可以鏈接toTex()方法,再由MathJax來呈現數學符號。
輸入框內的字串值轉換成Latex字串後,放入我們想置入的DOM後,便可由MathJax.typeset()將內容渲染為數學方程式符號。以下程式在文字輸入框輸入運算式按下Enter後,會自動呈現方程式字體。
<div class="group input-group">
<span class="addon input-group-addon">\(f(x)\)=</span>
<input type="text" id="expression">
</div>
<div class="group output-group">
<span class="addon output-group-addon">\(f(x)\)=</span>
<span class="result"></span>
</div>
const expression = document.querySelector('#expression')
const result = document.querySelector('.result')
const inputGroup = document.querySelector('.input-group')
const outputGroup = document.querySelector('.output-group')
expression.value = ''
expression.addEventListener('change', () => {
convert()
inputGroup.style.display = 'none'
outputGroup.style.visibility = 'visible'
})
outputGroup.addEventListener('click', () => {
outputGroup.style.visibility = 'hidden'
inputGroup.style.display = null
})
function convert() {
let latexExpr = math.parse(expression.value).toTex({
parenthesis: 'keep',
})
let options = MathJax.getMetricsFor(result)
options.display = false
result.innerHTML = `\\(${latexExpr}\\)`
MathJax.typeset([result], options)
}
MathJax.typset()的第二個參數是選擇性參數,用來設定渲染數學符號時的字體等相關參數,我們可以用MathJax.getMetricsFor()取得Dom的相關資訊。
非同步處理數學符號可以用MathJax.typesetPromise()或MathJax.tex2chtmlPromise();MathJax.tex2chtmlPromise()可以將Latex格式的字串,渲染成chtml數學符號字體,下面是我們的程式範例。
<div class="m-exercice" data-title="Exercise 1">
<div id="exercise-1">試計算下列函數的導數 \(f(x) = \dfrac{x+2}{x+4} \)</div>
<button id="show-answer">解答</button>
<div class="answer"></div>
</div>
當按下「解答」按鈕後,網頁抓取solutions.inc檔案,並讀取檔案內容存入字串變數中,再將內容渲染成數學符號。
const showAnswer = document.querySelector('#show-answer')
const answer = document.querySelector('.answer')
showAnswer.addEventListener('click', displayAnswer)
function displayAnswer() {
fetch(`./solutions.inc`)
.then ( (r) => { return r.text() } )
.then ( (s) => {
MathJax.tex2chtmlPromise(s)
.then(node => {
answer.innerHTML = ''
answer.appendChild(node)
MathJax.startup.document.clear()
MathJax.startup.document.updateDocument()
})
})
}
檔案solutions.inc的內容為
\begin{align*}
\left (\dfrac{f}{g} \right)' = \dfrac{f'g -g'f}{g^2}
\implies f'(x) &= \dfrac{(x+4) - (x+2)}{(x+4)^2} \\
&= \dfrac{2}{(x+4)^2}
\end{align*}
今日介紹math.js協助我們處理輸入運算式的解析,並轉成Latex語法,再由MathJax渲染;也了解在非同步的情形下,如何呼叫Mathjax.tex2chtml()來染渲,範例中非同步處理解答的部份,解答是由檔案中預存;整個範例如果能夠整合,是否可以直接將輸入框中的微分答案找出並顯示,應該是不錯的習題;math.js有提供方法,希望大家去找找看,明天我們將利用這些技巧來完成黎曼積分的範例。