A couple of suggestions for a little more generality; the `input` event could be used on the form itself, as it propagates the events down to the inputs. From there one can use the `HTMLFormElement` API to grab the sibling inputs and output.
Also when using the `input` event, it allows for the `<output>` elements to update on every change, and to show for example input errors in real-time.
For semantic markup, I would suggest using `<fieldset>` and `<legend>`, although they still suffer from getting special treatment from some browser vendors which can make styling difficult. I would also recommend using `<input type="number">`'s `min`, `max` and `step` attributes.
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
document.addEventListener('DOMContentLoaded', () => {
const outputs = document.querySelectorAll('output')
const using = (thing, fun) => fun(thing)
const average = ary =>
using(ary.map(x => Number(x.value)).filter(Boolean),
xs => (xs.reduce((x, y) => x + y, 0) / xs.length).toFixed(1))
const lastAsAverage = coll =>
using([...coll], xs => xs.pop().value = average(xs))
document.forms[0].addEventListener('input',
({target: {parentElement: {elements: inputs}}}) =>
[inputs, outputs].forEach(lastAsAverage))
})
<!-- language: lang-css -->
input:invalid { background-color: #faa }
<!-- language: lang-html -->
<form>
<fieldset>
<legend>Physics:</legend>
<input type="number">
<input type="number">
<input type="number">
<output></output>
</fieldset>
<fieldset>
<legend>History:</legend>
<input type="number">
<input type="number">
<input type="number">
<output></output>
</fieldset>
<output></output>
<input type="reset">
</form>
<!-- end snippet -->