📜 ⬆️ ⬇️

Here you are, grandmother, and namespaces

Experience of using namespaces in client XHTML


Text Rostislav Chebykina.


I brought you a package. But I will not give it to you, because you have no documents.
Postman Pechkin

Together with Denis Lesnov, we are developing an audio player for the site, which was already told here in 2015 . Now an updated version is on the way, which can play not only individual tracks, but also entire playlists.


This article is not about the player itself, but about the unexpected phenomena that we encountered when trying to use real XHTML.




The main goal of our project is to enjoy joint programming. Therefore, we write code the way we like it and use technologies that interest us.


For example, I was interested in XHTML since the W3C Consortium published the first drafts of this standard, and many enthusiasts rushed to promote the updated language. At that time, many hoped that the entire Internet was about to switch to XML-compatible markup, and from this there would be a universal happiness.


On my own website, the web pages always followed the syntax of XHTML and were sent with the application / xhtml + xml type to the browsers that supported it. A few years ago, I generally stopped giving text / html.


By now, the hype around XHTML has subsided, the corresponding working group of the Consortium has closed after several years of prostration, and then enthusiasts have switched to other fashionable concepts. I’m sorry that many promising XHTML 2.0 ideas haven’t been so widely implemented. For example, the standard proposed that any element could be turned into a hyperlink by assigning to it the href attribute:


 <li href="/about/"> </li> <li href="/contacts/"></li> 

Alas, this did not materialize, so for canonical idioms of web interfaces like “clickable” pictures, you still have to use the img element and the a element separately, as in the early 1990s.



Maybe XHTML “didn’t take off” because enthusiasts never managed to demonstrate its practical advantages. On fan sites, the XHTML code was purely cosmetic in nature and did not contain anything that plain HTML would not provide. On the contrary, XHTML limited the old-school developers by not allowing their favorite document.write and forcing to explicitly insert tbody into each table.


In 2016, I finally decided to try the endemic possibilities of XHTML, namely the namespace. I wanted the components of the audio player to be self-made elements in their own namespace:


image


As expected, the namespace and its prefix are declared in the opening html tag:


 <html xmlns="http://www.w3.org/1999/xhtml" xmlns:p="http://rostislav.chebykin.ru/xmlns" lang="ru" xml:lang="ru"> 

The introduction of self-made elements in the code did not cause any problems, but it did not bring any benefit. The benefit should have come after these elements will take on the appearance with CSS and come to life with JavaScript. And here began the hitch.



First, at the stage of the initial testing of the idea, it turned out that the style attribute does not work for homemade elements:


 <p:track style="background: #999;" /> //   

The corresponding attribute appears in the DOM tree, but the background element on the page is not painted in the specified color.


But when you connect a separate style sheet (in an external file or in the style element), the CSS successfully operates, and self-made elements are drawn using the namespace selector:


 p|track { background: #999; } 

However, for this to work, the first rule in the style sheet is to declare @namespace :


 @namespace p url('http://rostislav.chebykin.ru/xmlns'); 

Thus, we managed to decorate the track in the desired color and give the playhead element a round shape. But it still did not provide the dynamics, which is the essence of the player interface. And this dynamic led us into the wilds.



To work with namespaces in the DOM there are methods createElementNS and getElementsByTagNameNS . Their behavior in some places confuses me: for example, the p|track selector acts on an element regardless of whether the namespace prefix is ​​specified in the second argument of createElementNS :


 const ns = 'http://rostislav.chebykin.ru/xmlns'; document.createElementNS(ns, 'p:track'); document.createElementNS(ns, 'track'); //       p|track 

But getElementsByTagNameNS wants to get the name strictly without a prefix, no matter which of the two ways the element was created:


 document.getElementsByTagNameNS(ns, 'track'); //  HTMLCollection    document.getElementsByTagNameNS(ns, 'p:track'); //   HTMLCollection 

But it still does not matter. The trouble began further when we tried to animate the whole structure through JS.



Moving the head of the player - it would seem, what could be easier? Get the desired position, convert it to a percentage and assign it to the left property:


 playhead.style.left = pos; 

(Here, the playhead variable points to the desired element in the DOM, and pos is a percentage line in CSS format, such as '12.34%' ).


But no! It turns out that the playhead does not have a style property. To figure out why this happened, playhead compare the playhead prototype playhead with the standard HTML element chain:


 playhead: Element ← Node ← EventTarget ← Object div: HTMLDivElement ← HTMLElement ← Element ← Node ← EventTarget ← Object 

The style property, which provides access to the CSS properties of an element, is defined in the HTMLElement interface and is absent from its parent Element .


Trying to transfer the "native" style from the HTMLElement to homemade elements, we were defeated. Even if you just type in the console HTMLElement.prototype.style , an error message HTMLElement.prototype.style , and all the more so with this property you can’t do anything meaningful.


I had to use the CSS Object Model : dynamically connect external CSS, get to its rules, which have the same style property, and nail this style to the appropriate elements with nails:


 const link = document.createElement('link'); //   link'   //    head playhead.style = link.sheet.cssRules.item(1).style 

After this, constructs like playhead.style.left = pos; started working ... everywhere except Safari. Unexpectedly, it turned out that Element.prototype does have a style property in this extravagant browser, and the descriptors of this property do not allow it to be assigned anything. The problem was solved by redefining the style personally for our elements:


 Object.defineProperty(playhead, 'style', { writable: true }); 


Finally, a separate surprise was how the querySelector , querySelectorAll and matches methods that take CSS selectors as arguments are bypassed with namespaces, for example:


 document.querySelectorAll('p|track'); playhead.matches('p|playhead'); 

Here, each browser found its own words to express bewilderment:


EdgeNamespaceError
ChromeUncaught DOMException: Failed to execute 'matches' on 'Element': 'p | playhead' is not a valid selector
FirefoxSyntaxError: An invalid or illegal string was specified
SafariNamespaceError (DOM Exception 14): Namespaces in XML

The reason is that the listed methods cannot resolve the prefix p and associate it with the corresponding namespace. In XHTML, there is an xmlns attribute for declaring namespaces, @namespace in CSS, and @namespace in JavaScript! - there is nothing. Characteristically, the document.createNSResolver method works in browsers, but its result is not screwed to methods of the querySelectorAll type.


The Selectors API says that "namespace prefix needs to be resolved," but is immediately attributed: "This specification does not provide support for resolving arbitrary namespace prefixes." Interestingly, in the draft versions of the specification there was an NSResolver interface and a corresponding argument was proposed in the "selector" methods:


 Element querySelector(in DOMString selectors, in NSResolver nsresolver); 

However, on the way to the recommendation of the W3C, NSResolver was villainously drank, and as a result, the specification behaves like the postman Pechkin: “I brought you a package, only I will not give it to you, because you have no documents”.


I would not be surprised if in a few years the Consortium declares XHTML language deprecated and obsolete, and browsers refuse to support application / xhtml + xml. I hope that before this time Denis and I will be able to finish the next version of the audio player so that it will be preserved on the Internet at least as a museum piece.


')

Source: https://habr.com/ru/post/318984/


All Articles