Unscopable?: Frage zum Wiki-Artikel „scope“

problematische Seite

Bei Verwendung innerhalb eines DOM-API-Aufrufs – wie querySelector(), querySelectorAll(), matches() oder Element.closest() – entspricht :scope dem Element, auf das die Methode angewendet wurde.

Wenn das Element, auf dem :scope angewendet werden soll, ein DocumentFragment ist, dann schlägt dies aber (jedenfalls bei meinen Versuchen) grandios fehl. (Evtl. sucht das doch immer in :root?)

Ist das ein Fehler des (der) Browser? Oder was ist an einem `documentfragment.querySelector(':scope > element') falsch?

  1. problematische Seite

    Hallo Unscopable,

    danke für den Hinweis. Die Spezifikation sieht vor, dass das funktioniert, und macht dazu sogar extra ein Beispiel.

    Aber es sieht so aus, dass die Implementierung in den Browsern fehlt oder unvollständig ist. CSS wird normalerweise nicht auf Fragmente angewendet, deswegen fehlt da vermutlich der Support.

    Rolf

    --
    sumpsi - posui - obstruxi
    1. problematische Seite

      … CSS wird normalerweise nicht auf Fragmente angewendet …

      In Stylesheets (und angrenzenden Gebieten) wohl nicht. Aber fragment.querySelector… scheint mir jetzt nicht so abwegig. Auch deshalb, weil die Manipulationen dort „in Ruhe“ vorgenommen werden könn(t)en.

      Mir ist es jedenfalls aufgefallen, als ich eine utility-function gebastelt habe. In etwa

      function utility(root, query) {…}
      

      Und beim Herumspielen damit wollte das Ding irgendwie anders reagieren, je nachdem, ob das mit z.B. document.body oder fragment.replaceChildren(document.body.clone(true)gefüttert wurde.

      Ohne dies wäre es mir wohl kaum aufgefallen, denn dann fischt man so ein Fragment ja eher „halb fertig“ und sucht dann da eher nicht mehr nach :scope > div oder ähnlich verwurzeltem Kram.

      1. problematische Seite

        Dazu dann noch ein „Witz“: beim Versuch, meine „root-level-Selektion“ im Fragment anders umzusetzen, hat eine (vmtl. dubiose) Änderung dazu geführt, daß ich ein “Uncaught SyntaxError: missing variable name, got keyword 'this'” kassiere.

        Den Fehler muß ich jetzt suchen. Aber es geht ja um den Witz an der Sache. Und der steckt im Quelltext. Oder eigentlich ja nicht. Denn: im kompletten Script erscheint nicht ein einziges Mal this! Nicht mal als (sub)string in Parametern o.ä.

        Vermutung: (mindestens) eine der JS-Funktionen (ich vermute: DOM-API) ist tatsächlich keine Funktion sondern ein Makro (wie man es schon aus diesen „guten alten Zeiten“ von Assemblern oder auch K&R-C kennt). Und dieses packt dann, klammheimlich, das bemängelte this rein.

        1. problematische Seite

          Hallo Unscopable?,

          Nein. js hat keine Makros. Oder nutzt du TypeScript?

          Rolf

          --
          sumpsi - posui - obstruxi
          1. problematische Seite

            Hallo auch!

            Nein, ich verwende außer ein paar Erweiterungen (hier v.a. Tampermonkey und auch Stylus gerne Stylus) nichts extra.

            Den Fehler habe ich gefunden: ein } war ein Statement zu „hoch“ gelandet. Damit läuft der Teil jetzt und die Fehlermeldung ist weg. Aber wieso da this beklagt wurde …?

            Außerdem: „ich hab’ da noch einen“ („schon wieder“ meine Forrest):

            const marker = 'mimimi';	// … quasi ein `Symbol`
            const littleZip = (what) => {
            	let modified = false;
            	for( const eWalk = Walker(
            		what,
            		NodeFilter.SHOW_ELEMENT,
            		x => x.hasAttribute(marker)
            		?	NodeFilter.FILTER_SKIP
            		:	NodeFilter.FILTER_ACCEPT
            	);;) {
            		const e = eWalk.nextNode();
            		if( ! e ) break;
            
            		for(const wt of e.childNodes) {
            			if( ! wt ) continue;
            			if( wt.nodeType != Node.TEXT_NODE ) continue;
            			if( wt.data.trim().length == 0 ) wt.remove();
            			else {
            				const span = document.createElement('span');
            				span.setAttribute(marker, '');
            				span.textContent = wt.data;
            				wt.replaceWith(span);
            			}
            		}
            	}
            	root.querySelectorAll(`[${marker}]`).forEach(x => console.log('>',x));				// #1
            	root.querySelectorAll(`[${marker}]`).forEach(x => x.removeAttribute(marker));	// #2
            }
            

            macht, so von der Funktion her, was es soll. Aber: der Versuch, die Attribute (hier: „mimimi“) zu löschen schlägt auch fehl.

            Nach einigem Herumexperimentieren (z.B. den Selektor detailierter auszuformulieren: span[mimimi], :scope [mimimi] – z.T. sieht man das den .log-Aufrufen ja an) sieht es (erst Mal?) so aus, als ob in Fragmenten auch das Suchen nach Attributen mit Problemen behaftet ist.

            Nicht alle schlagen fehl. Attribute, die schon beim Erzeugen des Fragments mit eingepackt wurden, scheinen sichtbar zu sein. Aber „meine“? Die erscheinen „nur“ im HTML Sie lassen sich dann auch aus der Konsole heraus mit einem fast identischen Stück Code (aus dem Quelltext kopiert, root durch document.body ersetzt) löschen. Aber weder „#1“ noch „#2“ (s.o.) machen irgend etwas (bemerkbares).

            … 😳


            Ach so: Walker ist ein verkapptes (x,y,z) => createDocumentWalker(…) Und: der ist (war?) an der Stelle dafür gedacht, auch „Elemente zu zippen“, aber der Teil steht (noch?) aus. Ziel wäre es, verschachtelte Strukturen, die „wie eine Matroschka“ nichts weiteres enthalten als einen Wert, im wesentlichen auf diesen zu reduzieren.

          2. problematische Seite

            Jetzt habe ich, einigermaßen zufällig, eine Möglichkeit entdeckt, wie es zu so eine „this-Fehlermeldung“ kommen kann:

            Einfach den Code mit einem { „abschließen“!

            Das kann es, so, zwar nicht gewesen sein (zumindest hat der Linter nicht gemeckert), aber immerhin. Und wie damit JS zu einem beklagenswerten this kommt …? 🤷🏽‍♂️

    2. problematische Seite

      Und noch eins: selectNodeContents (Range) schlägt auch fehl, wenn man da als „Vater des Reiches“ ein Fragment einsetzen möchte:

      const fragment = new DocumentFragment();
      const selector = 'ol:has(+div>header+ol)';
      …
      
      for (
      	const odho = [ ... fragment.querySelectorAll(selector) ];
      		// klappt, da *nicht* nach '> ol…' gefragt wird
      		// = irgendwie seltsam!
      	odho.length;
      ) {
      	//	odho? Vgl. Selektor!
      	const ol = odho.shift();
      	const div = ol.nextElementSibling;
      	const hdr = div.firstElementChild;
      	const reihe = div.lastElementChild;
      
      	const frame = document.createElement('section');
      
      	const blockRange = new Range();
      	// blockRange.selectNodeContents(ol.parentElement); ➾ throw:
      	//	TypeError: Range.selectNodeContents:
      	//	Argument 1 is not an object.
      
      	//	blockRange.selectNode(ol);	// hilft auch nicht weiter
      	blockRange.setStartBefore(ol);	// klappt noch
      	blockRange.setEndAfter(reihe);	// †
      	//	Range braucht ein Elternteil, sonst stirbt es
      	//	an innerlicher Zerrissenheit:
      	//		InvalidStateError:
      	//		An attempt was made to use an object
      	//		that is not, or is no longer, usable
      
      	blockRange.surroundContents(frame);	// throws 
      …	// hier *sollten* die OL miteinander „gefilter-mischt“
      …	// und der Header nach vorne gestellt werden …