Web components pose an existential threat to the educational technology industry [hard stop]. By producing free, complex, and platform portable functionality, we can liberate educational experiences of traditionally entrenched, poorly constructed ecosystems that universities pay millions for.
For today's example of what's possible I built a simple "click the word" game for my students as a codepen. Here's the code and then we'll step into notable concepts of this you could use in your own projects
Implementation
<x-ample answers="implemented,because,is,going">This is a whole bunch of text that is going to be in the middle here but yet we won't see it because of the slot not being implemented</x-ample>
The implementation has the answer in it (OH NOZE!) but at run-time, reads this into a variable and deletes the answer from the dom.
There's two ways I accomplish this. The 1st is reading in the Light DOM as data via .innerText
. Because we're just trying to mark words we don't need the nodes that might live there.
this.textAry = this.innerText.split(/\s+/g);
Now we've got an array of text like ["word", "another"]
as we .split
using a regex to ensure all spaces / end-lines get picked up.
We do a similar practice with the answer. To hide the answer from the attributes in the DOM we listen for answers
to change in our updated()
life cycle of LitElement and split on ,
in order to generate another Array
.
updated(changedProperties) {
changedProperties.forEach((oldValue, propName) => {
if (propName === "answers" && this[propName]) {
this.validAnswers = this[propName].split(",");
// unset so that people can't cheat
this.answers = null;
}
});
}
Then when we want to make evaluations we use validAnswers as opposed to answers. Setting this to null
we wipe it from the Light DOM.
Rebuilding the "game" aspect
So now we've got an array of values that are correct, and an array of original text.
updated(changedProperties) {
changedProperties.forEach((oldValue, propName) => {
if (propName === "textAry") {
this.rebuildContents(this[propName]);
}
});
}
rebuildContents(ary) {
// wipe out inner
this.shadowRoot.querySelector("#area").innerHTML = "";
ary.forEach((word) => {
let span = document.createElement("span");
span.innerText = word;
span.setAttribute("tabindex", "-1");
span.addEventListener("click", this.clickWord.bind(this));
this.shadowRoot.querySelector("#area").appendChild(span);
});
}
.rebuildContents
takes an array of text and does the following:
- Wipes a shadowRoot
id
marked asarea
- Creates a
span
element - Sets
.innerText
to the word we're on in the array - Sets a tabindex so that keyboard users can focus the element
- add's an event listener for clicking.
When a span get's clicked, we toggle a data-selected
attribute on it which we can then visualize using a basic css selector
span[data-selected] {
outline: 2px solid purple;
}
Now we can toggle by "click" the status of each word so that we know what someone wants to mark for testing correctness in identification.
Testing answers
When we tap our Test button, we capture this event and then walk the shadowRoot for what the user selected previously. This attribute can then be used to do a .querySelectorAll
and obtain what the user clicked previously without us actually having stored that information!
testAnswers(e) {
const selected = this.shadowRoot.querySelectorAll("#area span[data-selected]");
console.log(selected);
for (var i=0; i < selected.length; i++) {
const el = selected[i];
if (this.validAnswers.includes(el.innerText)) {
el.setAttribute("data-status", "correct");
}
else {
el.setAttribute("data-status", "incorrect");
}
}
}
We then loop through these span[data-selected]
elements that matched our query and see if their innerText
was included in the initial validAnswers
array. If we get a match, we set (and visualize) correctness, otherwise we show incorrect.
While this element isn't finished, it's a great example of the transformative nature of modern standards. A previously unapproachable problem requiring Flash or Meta's R_act framework can now be achieved with a slim library like Lit and honestly could be VanillaJS with minor tweaks.
In project EdtechJoker my students explore problems like this. Using the Web component standard we can liberate pedagogical approaches like this to empower people to educate others regardless of access to overpriced platforms.
Top comments (1)
See: MDN: element.replaceWith