Nachricht für neue Nutzer.
Nachricht für engagierte Nutzer.

Widget:KnowHowComputer

Aus ZUM-Unterrichten

<script type="text/javascript">

       (function () {
           if (!customElements.get('know-how-computer')) {
               // Define the KnowHowComputer Web Component
               class KnowHowComputerElement extends HTMLElement {
                   DEFAULT_MEMORY_CONFIGURATION = [
                       {
                           name: 'Leer', program: {}
                       },
                       {
                           name: 'Standard Demo', program: {
                               '1': 'isz 8',
                               '2': 'jmp 4',
                               '3': 'stp',
                               '4': 'inc 7',
                               '5': 'dec 8',
                               '6': 'jmp 1',
                               '7': '5',
                               '8': '4'
                           }
                       }
                   ];


                   constructor() {
                       super();
                       this.attachShadow({mode: 'open'});
                       // Add the HTML template and styles to the shadow DOM
                       this.shadowRoot.innerHTML = `<style>
   :host {
       display: block;
       font-size: calc(15px + 0.390625vw);
   }
   :host > div {
       display: grid;
       gap: 1em;
       grid-template-areas:
           "title title"
           "nav  side   "
           "main side  ";
       > h1:first-child {
           grid-area: title;
       }
   }
   code {
       font-weight: bolder;
   }
   nav {
       grid-area: nav;
       display: flex;
       gap: 0.5em;
       > input {
           max-height: 2rem;
       }
   }
   main {
       grid-area: main;
       display: flex;
       flex-direction: column;
   }
   aside {
       grid-area: side;
   }
   details {
       font-size: 0.95rem;
   }
   details > summary {
       font-weight: bolder;
       font-size: 1.15rem;
   }
   @media (max-width: 420px) {
       :host > div {
           display: flex;
           flex-direction: column;
           grid-template-areas:
       "title" "nav" "main" "side";
       }
   }

</style>

Know-How-Computer

   <nav class="steueritem">
       <input type="button"
              id="neustartschalter"
              value="Neustart F8"
              aria-describedby="descr-neustartschalter">
       <input type="button"
              id="ausfuehrenschalter"
              value="Ausführen F9"
              aria-describedby="descr-ausfuehrenschalter">
   </nav>
   <main>
       <label for="programmzaehleranzeigefeld">Programmzähler</label>
       <input type="text"
              id="programmzaehleranzeigefeld"
              aria-describedby="descr-programmzaehleranzeigefeld"
              readonly
              disabled/>
       <label for="speicheranzeigefeld">Hauptspeicher</label>
       <textarea id="speicheranzeigefeld"
                 aria-describedby="descr-speicheranzeigefeld">
       </textarea>
       <label>Speicherkonfiguration laden:</label>
       <select id="memory-selector"></select>
   </main>
   <aside>
       <details open>
           <summary>Bedeutung der KHC Befehle</summary>
inc x
Erhöhe den Wert in Zelle x um 1
und erhöhe den Programmzähler um 1
dec x
Verringere den Wert in Zelle x um 1
und erhöhe den Programmzähler um 1
isz x
Wenn Zelle x den Wert 0 (zero) enthält,
dann erhöhe den Programmzähler um 2,
sonst erhöhe den Programmzähler um 1
jmp x
Setze den Programmzähler auf den Wert x
stp  
Beende das Programm
       </details>
       <details>
           <summary>Über den Know-How-Computer</summary>

Die Idee zum Know-How-Computer wurde im Jahr 1983 von Wolfgang Back (WDR) und Ulrich Rohde (PC-Magazin) entwickelt. Veröffentlicht wurde das Konzept des KHC u.a. in den Computerzeitschriften MC und PC-Magazin. Weitere Informationen findet man auf <a href="https://de.wikipedia.org/wiki/Know-how-Computer" target="_blank">de.wikipedia.org/wiki/Know-how-Computer</a>

Eine leicht abgewandelte Form des Know-How-Computers ist der so genannte Murmelrechner, der auf der Seite <a href="https://www.inf-schule.de/rechner/bonsai/murmelrechner" target="_blank"> www.inf-schule.de/rechner/bonsai/murmelrechner</a> vorgestellt wird.

Diese JavaScript-Implementierung des Know-How-Computers ist auch für Menschen mit Seheinschränkungen barrierefrei zugänglich. Sie wurde 2025 von Ulrich Kalina erstellt.

       </details>
       <details>
           <summary>Kurzanleitung zu dieser JavaScript Implementierung des KHC</summary>

Die Bedienoberfläche des KHC besteht in dieser Implementierung aus folgenden fünf Elementen, die mit TAB bzw. UMSCHALT+TAB fokussiert (angewählt) werden können.

Mehrzeiliges Textfeld Hauptspeicher

bildet den Hauptspeicher des Know-How-Computers ab. Jede Zeile des Textfeldes enthält die Nummer (Adresse) einer Speicherzelle und deren Inhalt. Dabei gehört die Adresse (Zahl vor dem Doppelpunkt) selbst nicht zum Inhalt der Speicherzelle, sondern dient lediglich ihrer eindeutigen Bezeichnung. Die Zelleninhalte (hinter dem Doppelpunkt) können entweder KHC-Befehle oder Daten (Zahlenwerte von 0 bis 99) sein.
Der gesamte Inhalt des Textfeldes kann über Tastatureingaben verändert werden. Dabei muss in jeder Zeile die Abfolge Adresse, Doppelpunkt, Befehl oder Datenwert eingehalten werden. Da es sich um ein normales Textfeld handelt, kann sein gesamter Inhalt, also ein ganzes KHC-Programm einfach durch Kopieren und Einfügen ("Drag and Drop") aus einem Texteditor in den KHC-Speicher importiert oder umgekehrt in einen Editor exportiert und in einer Textdatei gespeichert werden.
Der importierte Programmtext darf Kommentare enthalten. Diese beginnen mit einem Semikolon ; und reichen bis zum Zeilenende. Die Kommentare werden automatisch entfernt, wenn der Fokus das mehrzeilige Textfeld verlässt.
Wenn in diesem Textfeld Programmzeilen verändert wurden, sollte vor der nächsten Programmausführung ein Neustart mit dem Neustart-Schalter (F8) durchgeführt werden.

Einzeiliges Textfeld Programmzähler

enthält eine Kombination aus Programmzähler und Befehlsregister. Im Befehlsregister (hinter dem Doppelpunkt) steht immer der aktuelle Befehl, der im nächsten Schritt ausgeführt werden soll. Der Programmzähler (vor dem Doppelpunkt) gibt die Adresse der Speicherzelle an, von der dieser Befehl ins Befehlsregister geladen wurde.

Neustart-Schalter (F8)

setzt den Programmzähler auf den Wert 1 zurück und lädt den Befehl aus der Zelle 1 des Hauptspeichers in das Befehlsregister. Der Inhalt des Hauptspeichers wird durch einen Neustart nicht verändert. Der Neustart-Schalter kann auch mit der F8-Taste aktiviert werden.

Ausführen-Schalter (F9)

führt den aktuellen Befehl im Befehlsregisters aus und lädt anschließend den nächsten Befehl aus dem Hauptspeicher dorthin. Der Ausführen-Schalter kann auch mit der F9-Taste aktiviert werden.

       </details>
   </aside>

`;

                       // Create the KnowHowComputer instance
                       this.khc = new KnowHowComputer(this.shadowRoot);
                   }
                   refreshMemoryConfigurations() {
                       this.memoryConfigurations = [
                           ...this.DEFAULT_MEMORY_CONFIGURATION,
                           ...this.parseMemoryElements()
                       ];
                       this.refreshMemorySelector(this.memoryConfigurations);
                   }
                   // Lifecycle callbacks
                   connectedCallback() {
                       // Component is now in the DOM
                       // Parse khc-memory child elements
                       this.refreshMemoryConfigurations();
                       this.observer = new MutationObserver(mutations => {
                           this.refreshMemoryConfigurations();
                       });
                       this.observer.observe(this, {childList: true});
                       // Add event listener for memory selector
                       const memorySelector = this.shadowRoot.getElementById('memory-selector');
                       memorySelector.addEventListener('change', () => {
                           const selectedValue = memorySelector.value;
                           // Load program from khc-memory elements
                           const index = parseInt(selectedValue);
                           if (index >= 0 && index < this.memoryConfigurations.length) {
                               this.loadMemoryConfiguration(this.memoryConfigurations[index]);
                           }
                       });
                       // Set the initial selection in the memory selector
                       memorySelector.value = '0';
                   }
                   disconnectedCallback() {
                       // Component is removed from the DOM
                       // Clean up any event listeners if needed
                       if (this.observer) {
                           this.observer.disconnect();
                       }
                   }
                   // Parse khc-memory child elements
                   parseMemoryElements() {
                       // Use Array.from to convert HTMLCollection to Array
                       return Array.from(this.querySelectorAll('khc-memory'))
                           .map(element => {
                               const name = element.getAttribute('name') || `Program ${this.memoryConfigurations.length + 1}`;
                               const programText = element.textContent.trim();
                               const program = this.parseProgram(programText);
                               return {
                                   name: name,
                                   program: program
                               };
                           });
                   }
                   // Parse program text into a program object
                   parseProgram(programText) {
                       return Object.fromEntries(
                           programText.split('\n') // lines
                               .filter(l => !l.startsWith('#')) // filter out comments
                               .map(l => l.split(':', 2).map(s => s.trim())) // split and trim parts
                               .filter(([address, instruction]) => (address && instruction))) // filter incomplete lines;
                   }
                   // Populate memory selector dropdown
                   refreshMemorySelector(memoryConfigurations) {
                       const memorySelector = this.shadowRoot.getElementById('memory-selector');
                       while (memorySelector.childNodes.length) {
                           memorySelector.removeChild(memorySelector.firstChild);
                       }
                       // Add options for each memory configuration
                       memoryConfigurations.forEach((config, index) => {
                           const option = document.createElement('option');
                           option.value = (index).toString();
                           option.textContent = config.name;
                           memorySelector.appendChild(option);
                       });
                   }
                   // Load memory configuration
                   loadMemoryConfiguration(config) {
                       if (this.khc) {
                           this.khc.loadProgram(config.program);
                       }
                   }
               }
               // KnowHowComputer class that handles the emulator logic
               class KnowHowComputer {
                   constructor(containerNode) {
                       // Store the container node
                       this.container = containerNode;
                       // Configuration variables
                       this.speicheranzahl = 25; // Standardwert ist 25 für Adr. 1-25, kann aber geändert werden. Adr. 0 wird nicht benutzt
                       this.maxwert = 99;
                       this.spaltenanzahlinspeicheranzeigefeld = 10;
                       this.zeilenanzahlinspeicheranzeigefeld = this.speicheranzahl; // kann auch abweichend von speicheranzahl gewählt werden
                       // State variables
                       this.mem = new Array(this.speicheranzahl + 1); // mem[0] existiert, wird aber nicht benutzt
                       this.pz = 1; // Programmzähler
                       this.br = ""; // Befehlsregister
                       // Constants and utility variables
                       this.ziffern = "0123456789";
                       this.lueckenzeichen = " \r\n\t";
                       this.khcbuchstaben = "iszjmpincdecstp";
                       this.doppelpunkt = ":";
                       this.kommentarzeichen = ";";
                       this.khcbefehle = new Array("isz", "jmp", "inc", "dec", "stp");
                       // Lexer/parser state
                       this.symtypen = new Array();
                       this.symwerte = new Array();
                       this.symindex = -1;
                       // Error messages
                       this.fehlermeldungen = new Array();
                       this.fehlermeldungen[0] = "Keinen Fehler gefunden";
                       this.fehlermeldungen[1] = "Unerlaubtes Zeichen";
                       this.fehlermeldungen[2] = "Zahl erwartet";
                       this.fehlermeldungen[3] = "Adresse nicht im gültigen Bereich";
                       this.fehlermeldungen[4] = "Wert nicht im gültigen Bereich";
                       this.fehlermeldungen[5] = "Ungültiger Befehl";
                       this.fehlermeldungen[6] = "Syntaxfehler: Doppelpunkt erwartet";
                       this.fehlermeldungen[7] = "Syntaxfehler: Zahl oder Befehl erwartet";
                       this.fehlermeldungen[8] = "Syntaxfehler: stp ohne Operandenadresse ";
                       this.fehlermeldungen[9] = "Syntaxfehler: Operandenadresse erwartet";
                       this.fehlermeldungen[10] = "Laufzeitfehler: Kein ausführbarer Befehl";
                       this.fehlermeldungen[11] = "Laufzeitfehler: Operand würde zu klein";
                       this.fehlermeldungen[12] = "Laufzeitfehler: Operand würde zu groß";
                       // Initialize the emulator
                       this.init();
                   }
                   // Helper methods
                   meldung(index) {
                       alert(this.fehlermeldungen[index]);
                   }
                   stringmitleerzeichenauffuellen(str, bislaenge) { // neu 24.01.2024
                       return str.trimEnd().padEnd(bislaenge);
                   }
                   rechtsbuendig(s) {
                       return String(s).trimStart().padStart(2)
                   }
                   zahltrimmen(zahlstr, min, max) {
                       var test = parseInt(zahlstr);
                       if (isNaN(test)) {
                           test = min;
                       } else {
                           if (test < min) {
                               test = min;
                               this.meldung(11);
                           }
                           if (test > max) {
                               test = max;
                               this.meldung(12);
                           }
                       }
                       return test;
                   }
                   zeichenistinstring(z, s) {
                       return s.indexOf(z) !== -1;
                   }
                   arrayleeren(a) {
                       a.splice(0)
                       return a;
                   }
                   // Lexer and parser
                   khclexer(eingabe) {
                       var z; // einzelnes Zeichen aus eingabe
                       var i;
                       this.symtypen = this.arrayleeren(this.symtypen);
                       this.symwerte = this.arrayleeren(this.symwerte);
                       this.symindex = -1;
                       var symwert = "";
                       var symtyp = "l"; // z =zahl, b =buchstabenkette, l =luecke, : =doppelpunkt
                       eingabe = " " + eingabe + " ";
                       var ztyp = "l";
                       for (i = 0; i < eingabe.length; i++) {
                           z = eingabe.charAt(i);
                           if (this.zeichenistinstring(z, this.lueckenzeichen)) ztyp = "l";      // z ist ein Lückenzeichen
                           else if (this.zeichenistinstring(z, this.ziffern)) ztyp = "z";       // z ist eine Ziffer
                           else if (this.zeichenistinstring(z, this.doppelpunkt)) ztyp = ":";   // z ist ein Doppelpunkt
                           else if (this.zeichenistinstring(z, this.khcbuchstaben)) ztyp = "b"; // z kommt in einem der KHC-Befehle vor
                           else if (this.zeichenistinstring(z, this.kommentarzeichen)) ztyp = ";";  // z ist Kommentarzeichen
                           else return false;
                           if (symtyp == ztyp) { // aktueller symtyp stimmt mit dem ztyp des gerade gelesenen Zeichens z überein
                               symwert = symwert + z;
                           } else { // neues Symbol:
                               if (symtyp != "l") { // bisheriges Symbol in Arrays speichern
                                   this.symindex++;
                                   this.symwerte[this.symindex] = symwert;
                                   this.symtypen[this.symindex] = symtyp;
                               }
                               if (ztyp != ";") {
                                   symtyp = ztyp; // Anfang des neuen Symbols in sym-Vars speichern
                                   symwert = z;
                               } else { // Kommentarzeichen, also aufhören:
                                   return true;
                               }
                           }
                       }
                       return true;
                   }
                   istkhcbefehl(s) {
                       var ok = false;
                       for (let j = 0; j < this.khcbefehle.length; j++) {
                           if (s == this.khcbefehle[j]) {
                               ok = true;
                           }
                       }
                       return ok;
                   }
                   istgueltigezahl(zahlstr, min, max) {
                       var ok = true;
                       var test = parseInt(zahlstr);
                       if (isNaN(test)) ok = false;
                       else {
                           if (test < min || test > max) ok = false;
                       }
                       return ok;
                   }
                   khcparser(zeile) {
                       // Rückgabewert: fehlerindex
                       var ok = this.khclexer(zeile);
                       if (this.symwerte.join("").length == 0) return -1; // Leerzeile liefert keine Fehlermeldung
                       if (!ok) return 1; // "Unerlaubtes Zeichen"
                       if (this.symtypen[0] != "z") return 2; // "Syntaxfehler - Zahl erwartet"
                       if (!this.istgueltigezahl(this.symwerte[0], 1, this.speicheranzahl)) return 3; // "Adresse nicht im gültigen Bereich"
                       if (this.symtypen[1] != ":") return 6; // "Syntaxfehler - Doppelpunkt erwartet"
                       if (this.symtypen[2] != "z" && this.symtypen[2] != "b") return 7; // "Syntaxfehler - Zahl oder Befehl erwartet"
                       if (this.symtypen[2] == "z") {
                           if (!this.istgueltigezahl(this.symwerte[2], 0, this.maxwert)) return 4; // "Wert nicht im gültigen Bereich"
                       }
                       if (this.symtypen[2] == "b") {
                           if (!this.istkhcbefehl(this.symwerte[2])) return 5; // "Ungültiger Befehl"
                           if (this.symwerte[2] == "stp" && this.symtypen.length > 3) return 8; // "Syntaxfehler"
                           if (this.symwerte[2] != "stp") {
                               if (this.symtypen[3] != "z") return 9; // "Syntaxfehler"
                               if (!this.istgueltigezahl(this.symwerte[3], 1, this.speicheranzahl)) return 3; // "Adresse nicht im gültigen Bereich"
                           }
                       }
                       return 0; // keinen Fehler gefunden
                   }
                   // Initialization and control
                   init() {
                       for (let i = 0; i < this.speicheranzahl + 1; i++) {
                           this.mem[i] = "0";
                       }
                       /**@type HTMLInputElement */
                       const registeranzeigefeld = this.container.getElementById("programmzaehleranzeigefeld");
                       /**@type HTMLTextAreaElement */
                       const speicheranzeigefeld = this.container.getElementById("speicheranzeigefeld");
                       speicheranzeigefeld.rows = this.zeilenanzahlinspeicheranzeigefeld;
                       speicheranzeigefeld.cols = this.spaltenanzahlinspeicheranzeigefeld;
                       registeranzeigefeld.size = this.spaltenanzahlinspeicheranzeigefeld;
                       this.neustart();
                       // Set up event listeners
                       this.setupEventListeners();
                   }
                   setupEventListeners() {
                       const self = this;
                       this.container.getElementById('ausfuehrenschalter').addEventListener('click', function () {
                           self.befehlausfuehren();
                       });
                       this.container.getElementById('neustartschalter').addEventListener('click', function () {
                           self.neustart();
                       });
                       this.container.getElementById('speicheranzeigefeld').addEventListener('change', function () {
                           self.speicherfeldanzeigeinspeicheruebernehmen();
                           self.neustart();
                       });
                       this.container.getElementById('speicheranzeigefeld').addEventListener('focus', function () {
                           self.zeileinspeicheranzeigefeldmarkieren(self.pz);
                       });
                       this.container.addEventListener('keydown', function (event) {
                           if (event.keyCode === 120) { // F9
                               self.registeranzeigefeldaktualisieren();
                               self.befehlausfuehren();
                           } else if (event.keyCode === 119) { // F8
                               self.speicherfeldanzeigeinspeicheruebernehmen();
                               self.neustart(); // 24.01.2024
                           }
                       });
                   }
                   // Load a program from a program object
                   loadProgram(program) {
                       // Reset memory
                       for (let i = 0; i < this.speicheranzahl + 1; i++) {
                           this.mem[i] = "0";
                       }
                       this.mem[0] = "nicht verwendet";
                       // Load program into memory
                       for (const [address, instruction] of Object.entries(program)) {
                           if (parseInt(address) >= 1 && parseInt(address) <= this.speicheranzahl) {
                               this.mem[address] = instruction;
                           }
                       }
                       // Restart the emulator
                       this.neustart();
                   }
                   neustart() {
                       this.pz = 1; // Programmzähler
                       this.br = this.mem[this.pz]; // Befehlsregister
                       this.anzeigeaktualisieren();
                       this.container.getElementById("speicheranzeigefeld").focus();
                   }
                   registeranzeigefeldaktualisieren() {
                       var str = this.rechtsbuendig(this.pz) + ": " + this.stringmitleerzeichenauffuellen(this.mem[this.pz], this.spaltenanzahlinspeicheranzeigefeld - 3);
                       this.container.getElementById("programmzaehleranzeigefeld").value = str;
                   }
                   anzeigeaktualisieren() {
                       // Anzeige des gesamten Speichers aktualisieren:
                       const speicherfeld = this.container.getElementById("speicheranzeigefeld");
                       speicherfeld.value = this.mem.slice(1).map((speicher, idx) => this.rechtsbuendig(idx + 1) + ": " + speicher).join('\n');
                       this.registeranzeigefeldaktualisieren();
                       this.zeileinspeicheranzeigefeldmarkieren(this.pz);
                   }
                   befehlausfuehren() {
                       var fehlerindex = 0;
                       this.speicherfeldanzeigeinspeicheruebernehmen();
                       console.log("mem[pz] = " + this.mem[this.pz]);
                       this.br = this.mem[this.pz];
                       var z = parseInt(this.br);
                       console.log("Start befehlausfuehren mit z = parseint(br): " + z);
                       console.log("in befehlausfuehren vor Test: pz = " + this.pz + " br = " + this.br);
                       // Test: Ist Inhalt von br nur eine ganze Zahl, also ein Operand? Dann keine Ausführung möglich ...
                       if (!isNaN(z)) {
                           fehlerindex = 10;
                           this.meldung(fehlerindex);
                       } else {
                           // sonst gehen wir von einem Befehl aus:
                           var befehlscode = this.br.substr(0, 3).toLowerCase();
                           // Bei stp ist keine Adresse erforderlich und es wird auch nichts gemacht:
                           if (befehlscode == "stp") return 0;
                           // sonst muss eine ganze Zahl als Adresse folgen:
                           var adresse = parseInt(this.br.substr(4)); // ab Pos. 4 bis zum Ende ausschneiden
                           if (isNaN(adresse)) return 2;
                           this.pz = parseInt(this.pz);
                           switch (befehlscode) {
                               case "jmp":
                                   this.pz = adresse;
                                   break;
                               case "isz":
                                   if (this.mem[adresse] == 0) this.pz = this.pz + 2; else this.pz = this.pz + 1;
                                   break;
                               case "inc":
                                   let wert = this.mem[adresse];
                                   wert = parseInt(wert) + 1;
                                   wert = this.zahltrimmen(wert, 0, this.maxwert);
                                   this.mem[adresse] = wert;
                                   this.pz = this.pz + 1;
                                   break;
                               case "dec":
                                   let wert2 = this.mem[adresse];
                                   wert2 = parseInt(wert2) - 1;
                                   wert2 = this.zahltrimmen(wert2, 0, this.maxwert);
                                   this.mem[adresse] = wert2;
                                   this.pz = this.pz + 1;
                                   break;
                               default:
                                   fehlerindex = 3;
                           }
                           this.pz = this.zahltrimmen(this.pz, 1, this.speicheranzahl);
                           this.br = this.mem[this.pz];
                           this.anzeigeaktualisieren();
                       }
                       this.container.getElementById("speicheranzeigefeld").focus();
                       this.zeileinspeicheranzeigefeldmarkieren(this.pz);
                       return fehlerindex;
                   }
                   einezeileinspeicheruebernehmen(zeile) {
                       zeile = zeile.toLowerCase();
                       var fehlerindex = 0; // vorläufig
                       fehlerindex = this.khcparser(zeile);
                       if (fehlerindex == 0) {
                           var pz = parseInt(this.symwerte[0]);
                           var befehlszeile = this.symwerte[2];
                           if (this.symwerte.length > 3) befehlszeile = befehlszeile + " " + this.symwerte[3];
                           befehlszeile = this.stringmitleerzeichenauffuellen(befehlszeile, this.spaltenanzahlinspeicheranzeigefeld - 3);
                           this.mem[pz] = befehlszeile;
                       } else {
                           if (fehlerindex > 0) { // -1 wäre Leerzeile
                               alert(this.fehlermeldungen[fehlerindex] + " in\n" + zeile);
                           }
                       }
                   }
                   speicherfeldanzeigeinspeicheruebernehmen() {
                       var zeilen = this.container.getElementById("speicheranzeigefeld").value.split("\n");
                       for (let i = 0; i < zeilen.length; i++) {
                           this.einezeileinspeicheruebernehmen(zeilen[i]);
                       }
                       this.anzeigeaktualisieren();
                   }
                   setzeMarkierungInTextFeld(domElemSelector, posStart, posEnde) {
                       const domElem = this.container.querySelector(domElemSelector);
                       if ('selectionStart' in domElem) {
                           domElem.selectionStart = posStart;
                           domElem.selectionEnd = posEnde;
                           domElem.focus();
                       } else { // Internet Explorer vor Version 9
                           var inputRange = domElem.createTextRange();
                           inputRange.moveStart("character", posStart);
                           inputRange.collapse();
                           inputRange.moveEnd("character", posEnde - posStart);
                           inputRange.select();
                       }
                   }
                   zeileinspeicheranzeigefeldmarkieren(pz) {
                       let pzstring = this.rechtsbuendig(pz) + ": ";
                       let startpos = this.container.getElementById("speicheranzeigefeld").value.indexOf(pzstring);
                       let endepos = startpos + this.mem[pz].length + 4;
                       this.setzeMarkierungInTextFeld("#speicheranzeigefeld", startpos, endepos);
                   }
                   getCaretPos(domElemSelector) {
                       var pos = 0;
                       const domElem = this.container.querySelector(domElemSelector);
                       if (document.selection) {
                           domElem.focus();
                           var sel = document.selection.createRange();
                           sel.moveStart('character', -domElem.value.length);
                           pos = sel.text.length;
                       } else if (domElem.selectionStart || domElem.selectionStart == '0') {
                           pos = domElem.selectionStart;
                       }
                       return pos;
                   }
                   setzeCaretAnZeilenanfang(domElemSelector, zeilennr) {
                       const domElem = this.container.querySelector(domElemSelector);
                       let pos = domElem.value.indexOf(zeilennr + ": ");
                       this.setzeMarkierungInTextFeld(domElemSelector, pos, pos);
                   }
               }
               // Register the custom elements
               customElements.define('know-how-computer', KnowHowComputerElement);
           }


           if (!customElements.get('khc-memory')) {
               // Define the KhcMemoryElement custom element
               class KhcMemoryElement extends HTMLPreElement {
                   constructor() {
                       super();
                       // No need for shadow DOM as this is just a data container
                   }
                   // Lifecycle callbacks
                   connectedCallback() {
                       // Hide the element by default as it's just a data container
                       this.style.display = 'none';
                   }
               }
               customElements.define('khc-memory', KhcMemoryElement, {extends: 'pre'});
           }

})(); </script>

<know-how-computer></know-how-computer>