Blog-Archiv

Samstag, 29. Juli 2017

Sneaking into HTML Web Components

The specification of the HTML document object model (DOM) is now subject to several Internet sites:

  1. The old w3c (World Wide Web Consortium) has been reinforced by
  2. whatwg's (Web Hypertext Application Technology Working Group) DOM Living Standard - this can change every day!

Together with the upcoming ES-6 language, Web Components provide web page composition from reusable HTML fragments, with their CSS shielded against the host page's CSS. Grandparent of all web components is the <video> player. It was implemented by browser vendors as custom-element, and now this implementation way gets standardized and usable for all.


This Blog is a short sneak into the current state of Web Components technology. It shows how we can use it in browsers that already support it, or together with various "polyfill" libraries like Polymer. Chrome already implements all of it, on Firefox you must enable it explicitly, but it does not yet provide HTML imports, and only version 0 of the shadow-DOM. Mind that when you enable this in Firefox, the Web Components page will not render any more!

Example

Here is a screenshot of what my example code below will provide.

The first blue element is a normal DIV inside the main page. It is there to show how a DIV is styled by the main CSS: blue background color, 1 em padding, black border.

The second green DIV is a web component loaded from an external source. It has its own CSS, green color, 2 em padding, and no border, demonstrating that the main CSS can not intrude it.
It also has a script that is executed as soon as the component gets connected to the DOM. This script can access the whole document where it got imported, it is not restricted to its component.

The third blue DIV is also an imported component, but it has no own styling. It is there to show that the main CSS can affect it in this case, i.e. give it the CSS-inherited blue color, although not the 1 em padding (non-inherited CSS property).

The red DIV finally is a component that is inside a template element, directly in the document, not imported. This is the only one you would see in Firefox, because it did not implement the HTML import specification yet.

Host Page

I will present the source code of the main page in four parts. Please assemble them all into a file webbcomponents.html to try it out.

Head

First the main page head, containing the imports of two web components, and a CSS styling that, although strong, should not intrude the web components.

<!DOCTYPE HTML>
<html>
<head>
    <meta name="viewport" content="initial-scale=1"/>
    <meta charset="UTF-8"/>
    <title>Web Components Experiments</title>

    <link id="link-1" rel="import" href="webcomponent-1.html">
    
    <link id="link-2" rel="import" href="webcomponent-2.html">
    
    <style>
        /* try to affect the CSS of used web-components by some strong rules */
        div    {
            background-color: lightBlue !important;
            padding: 1em !important;
            border: 1px solid black !important;
        }
    </style>
    
</head>

Hosts

Next are the HTML elements that will receive the web components. They are called "host elements". Their content can be put into the web component using "slot" attributes. That means that the web component would take over the styling of that content, exclusively.

If there are no slot references, the content would get lost as soon as the web component is put into the shadow root of the host element. You can see this at host-1, its <h1> is not visible (see screenshot above).

<body>

    <div>This is a blue DIV in main page.</div>
    
    <div id="host-1">    <!-- must not be self-closing !? -->
     <h1>I am content of host-1.</h1> 
    </div>
    
    <div id="host-2">
     <h2 slot="slot-2">I am content of host-2.</h2>
    </div>
    
    <div id="host-3">
     <h3 slot="slot-3">I am content of host-3.</h3>
    </div>

Internal Web Component (3)

Here is the red DIV, as embedded web component inside a template element. The browser's DOM parser finds templates, but it ignores them and everything that is inside. They must be handled by JavaScript.

    <template id="component-3">
        <style>
            div    {
                background-color: Tomato;
                padding: 1em;
            }
        </style>
    
        <div>
            <div id="inner">I am the embedded "component-3" DIV, in red, and I show a "slot" of my "host" element:</div>
        
            <slot name="slot-3"></slot>
        </div>
        
    </template>

Mind how slot-3 refers to the content of the host element!

Script

Finally, a JavaScript is needed to create shadow roots and put the web components into their hosts. (Wouldn't they be in the shadow, the CSS of the host page would affect them!)

    <script>
        var render = function(hostId, templateContent) {
            var host = document.getElementById(hostId);
            try {
              var shadowComponent = host.attachShadow({ mode: "open" });
            }
            catch (error) {
              var shadowComponent = host.createShadowRoot();
            }
            shadowComponent.appendChild(templateContent);
        };
        
        var resolve = function(querySelectorElement, componentId) {
            var template = querySelectorElement.querySelector("template#"+componentId);
            return template.content;
        };
        
        var resolveEmbedded = function(componentId) {
            return resolve(document.body, componentId);
        };
        
        var resolveLink = function(linkId, componentId) {
            var link = document.querySelector("link#"+linkId);
            var linkedDocument = link.import;  /* wasn't "import" a reserved word? */
            if ( ! linkedDocument )
                throw "HTML import not found: "+linkId;
            return resolve(linkedDocument, componentId);
        };
        
        window.addEventListener("load", function() {
            render("host-3", resolveEmbedded("component-3"));
            render("host-1", resolveLink("link-1", "component-1"));
            render("host-2", resolveLink("link-2", "component-2"));
        });
        
    </script>

</body>
</html>

The render(hostId, templateContent) function finds the host element, creates a shadow root there, and puts the given web component into it. The attachShadow({ mode: "open" }) function is shadow-specification version 1, the createShadowRoot() fallback function is the old version 0.

The resolve() function fetches a web component from its template. Some implementations use document.importNode(content) to clone it.

The resolveLink() function fetches an imported web component from its template. Mind that we need a lot of ids for a web component: the link, the host, the template, all these must have ids for the JavaScript that builds them together.

External Web Components

Put following into files webbcomponent-1.html and webbcomponent-2.html in same directory as webbcomponents.html.

Component 1

This web component demonstrates how its local CSS dominates the CSS of the main page. Although DIVs are styled to blue in main page, even using !important, the component's DIV is green.

Further a script is there. Here the global variable document is the main page (webcomponents.html), not the component. The script writes a message into the main page's first DIV. Then it gets its local DOM from document.currentScript.ownerDocument, and writes another message there. Mind that you get a "Document Fragment" from template.content, not a DOM element.

<!DOCTYPE HTML>
<html>

    <template id="component-1">
        <style>
            div    {
                background-color: lightGreen;
                padding: 2em;
            }
        </style>
        
        <div>I am the imported "component-1" DIV in green!</div>
        
    </template>

    <script>
    (function() {
        var firstDiv = document.getElementsByTagName("DIV")[0];
        firstDiv.innerHTML = firstDiv.innerHTML + "<br>Script-1 found first DIV in document!";
        
        var componentDocument = document.currentScript.ownerDocument;
        var myDiv = componentDocument.querySelector("template").content.querySelector("div");
        myDiv.innerHTML = myDiv.innerHTML + "<br>Script-1 found its own DIV!";
    })();
    </script>
        
</html>

Component 2

I added this second web component to prove that any number of components can be imported. It demonstrates that also imported components can receive the content of their host elements via slot references. It also shows that the host page's CSS is active here, because no explicit styles were set by the component.

<!DOCTYPE HTML>
<html>

 <template id="component-2">
 
     <div id="inner">I am the imported "component-2" DIV, with no explicit style, and I show a "slot" of my "host" element:</div>
     
     <slot name="slot-2"></slot>
     
 </template>

</html>

By the way: you don't need the enclosing <html> tag, not even the <!DOCTYPE HTML> ....

Resume

What to learn?

  • CSS of a web component in a shadow root is local
  • JavaScripts are not
  • Web components can render contents of the page they are imported into
  • Content in an host element must refer to a slot in the component (using the slot's name), else it will get lost

Will web components be used? When browsers will implement them according to the specification, yes.

One of the most common use cases will be to have local CSS, while content comes from the main page. This gives you separation of content and presentation, and it relieves the problems with global monolithic CSS, which aggregates to sometimes uncontrollable complexity in AJAX-driven single page applications.




Sonntag, 23. Juli 2017

Separate HTML, CSS and JS

The aunt of our chief-programmer has a cousin that has a son that just made the grade and now learnt CSS. He will come for two months and polish the CSS of the web-application we are working on.

Alarm bells ringing. Why are we afraid of that? Didn't we separate HTML from CSS and JavaScript cleanly, so that the guy can't break anything, whatever he may commit?

No, we didn't. We just tried to. It is not possible to separate these things completely (although some seem to believe that:-).

In this Blog I will go through some thoughts about decoupling HTML, JS and CSS.

Three Technologies

  1. HTML - a markup language for text and multimedia content, the main thing that makes up a web page. Restricted in its interactive capabilities (just HTML forms). It provides a hierarchical structure of document-elements , which can have attributes, of which the id and class attributes are special, because they can be elegantly addressed by CSS rules. CSS inside the style attribute will be applied to the element with high precedence. HTML elements define a default layout behaviour (block- and inline-elements) that can be changed by CSS only. Some elements like <table>, <ul> (list), <h1> - <h6> and <blockquote> are dedicated to layout, some like <b>, <i>, <code> etc. are dedicated to styling.

  2. CSS - a rule-based language that covers "skinning" (color, font, border-style) and layout (positioning, alignment, margin, border-thickness, padding, tables, resize- and scroll-behaviour). CSS also can add text or images to the page via the pseudo-element's content property.

  3. JavaScript (JS) - a typeless structured script language used to dynamically work on a web page, primarily intended for processing user inputs, but it can do nearly everything with the web page. A JS interpreter is built into any browser, and it is instantiated for every displayed page. That means when you reload a page, its JS engine is dismissed, and a new one is created (dismissing all global variables). JS engines can not access sibling pages in the browser. JS can read and write CSS, thus it does not have an element.setForegroundColor() function. JS works with HTML elements generically, that means there is no type-specific function document.getDivById(), just a general document.getElementById().

Separation of Concerns?

What are the concerns?

  • Content
    expected in HTML

  • Layout
    expected in .... CSS? HTML?

  • Styling (skinning, the "Look" of Look & Feel)
    expected in CSS

  • Interaction (keyboard and mouse responses, the "Feel" of Look & Feel)
    expected in JavaScript

Hard to keep these expectations.

HTML

An HTML div block-element affects layout differently than a span inline-element, and a CSS property like width will not work any more when changing the type of its targeted element from div to span. HTML table specifies layout, as lists do (ul, ol, dl).

This is the biggest weakness of web-pages:

  • Layout has not been confined as a separate aspect

Although it is so important. We should have given it its own domain-specific language! Instead it is in both HTML and CSS.

Have you ever tried to set a text-field disabled using CSS? It is not possible. You need to do this using the HTML attribute disabled.

CSS

CSS addresses elements using rules, evaluated dynamically. But rules never can be really precise. HTML elements could have been added to the document, not being addressed by out-dated CSS rules. Thus errors always will be present where rule engines are working. The browser does not report them.

Although CSS is a programming language, it does not provide even basic means, which is the use of variables and arithmetic operations. That makes up the second weakness of web-pages:

  • CSS is so weak that it forces code duplications

Clever developers recommend that CSS should be applied through CSS classes only. That means, do not write CSS into the style attribute of HTML elements, just put a CSS-class identifier into the HTML element's class attribute and apply CSS that way.

Now when you look at actual web pages, you won't find a "definition" of a CSS class (like in Java or C++). A CSS class is just an identifier that tries to express a responsibility, and it is used in CSS rules, together with other criteria, and it is used in class attributes, together with other CSS classes. Where should programmers document the CSS classes that they have created?

  • There is no place for defining and documenting CSS classes

I consider self-documenting code to be a myth. The name alone is not enough, we need context.

Some people may also have spent a lot of time by internationalizing texts that were coded into the content property of pseudo-elements. Text definitely is not the responsibility of CSS, is it?

CSS even provides a very primitive kind of event listening through pseudo-classes. CSS menus are made of this, and their designers still consider them to be user-friendly.

JS

JavaScript finally is the tool that can manage everything. But you create lots of dangerous dependencies when writing JS code for a web page. You may anticipate a certain HTML element structure that is changed the very next months by a web designer. Same with CSS. From JS you can easily set styles on elements, but these will overrule what the web designer has put on them.

Now there are tips about how to separate JS and CSS. JS should add or remove CSS classes on the element, but never directly define any CSS property. So, never do element.style.color = "red" in JS, because this may not comply to the CSS theme.

But what about statements like element.style.width = "20em"; where 20 is a calculated width? Or does it makes sense to use a CSS class display-none for making an element disappear (like you would do in JS via element.style.display = "none"), when a CSS designer could turn this afterwards to display-none { visibility: "hidden"; }? This definitely changes the layout behaviour, which may not have been the intent of the JS programmer.

  • Keeping CSS out of JavaScript makes sense only for skinning (color, font), but not for layout

JavaScript is the almighty power in a web page. It can do everything. It will be hard to keep HTML and CSS out of it.

So What Should We Do?

Although it is not an overall solution, CSS classes are a way to achieve a minimum of decoupling. Never use the style attribute of an HTML element, better add a CSS class identifier to its class attribute. Then try to have that CSS class identifier alone on the left side of the CSS rule, not mixed with other criteria. That way you also have a place where you could document the CSS class, which is absolutely needed.

Avoid complex selectors in CSS and JS. Instead put CSS classes onto HTML elements extensively, and find descriptive names for them.

A CSS class should have just one responsibility. If there are more things to do, create further CSS classes. Keep their names according to the responsibility, and try to stay simple and human-readable.

Now there are advices to use prefixes for CSS classes. The prefix should express whether the class is used as JavaScript hook or for CSS styling. That is a good idea.

But concerning prefixes, this is also the only way to make CSS "local". In other words, we should use a constant CSS class prefix for our web-application, so that we can keep our CSS apart from common CSS libraries like bootstrap. There is even more to this, because we compose our application from components that also have names, and we would like to know which component the CSS on our single-page-application comes from.

Summarizing this, here is my CSS class prototype:

/**
 * Gray out the element.
 */
.myapp_mycomp_css_gray-out
{
  opacity: 0.6;
}

Prefixes explained:

  • _
    using underscore "_" as prefix-separator to reserve dash "-" for the responsibility

  • myapp
    distinguishes it from CSS that does not care about other CSS (like bootstrap)

  • mycomp
    we need to know which reusable component this comes from

  • css
    expresses that this CSS class is not used by JavaScript, e.g. to install event listeners

  • gray-out
    the responsibility of the CSS class

Don't say this is ridiculous. It is the consequence of 20 years web development :-)

Summary

Things are getting better. SCSS is relieving the lack of variables and arithmetic in CSS. ES-6 will make JS a better programming language. But will this really help us to decouple HTML, CSS and JS in our web pages?




Freitag, 14. Juli 2017

CSS Inline Alignment

Some things are so complicated that you do not want to go into them. I really appreciate that there are people that do this, and leave us a trace of what we could do to mitigate our daily problems with web browser CSS. Try to read this article about inline-formatting (link).

This Blog is about problems that you meet when you care about text alignment, that means you want to center everything vertically, even text in tables.

Example

The situation I want to talk about is having a table with a constant row height, and various contents inside. Let it look like this:

C
entering not done here
!

Source code is here:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<!DOCTYPE HTML>
<html>
<head>
  <meta charset="UTF-8"/>
  <meta name="viewport" content="width=device-width, initial-scale=1"/>
  <title>Aligning Inline Text</title>
  
  <style type="text/css">
    div.box  {
      border: 1px solid gray;
      height: 80px;
    }
  </style>
  
</head> 
<body>

  <table style="vertical-align: initial;">
    <tr>
      <td>
        <div class="box" style="font-size: 40px;">C</div>
      </td>
      <td>
        <div class="box">entering not done here</div>
      </td>
      <td>
        <div class="box" style="font-size: 60px;">!</div>
      </td>
    </tr>
  </table>
  
</body>
</html>

The browser calculates the table's row height from its highest cell, and all cells have been set to 80 pixels by the CSS rule div.box in head styling.

The 1st column contains a "C" of 40 pixels font-size, the text in 2nd column has default size, and the biggest "!" text is in 3rd column, having 60 pixels.

1st and 2nd column display a little bit strange, hanging on top. Should we change that? Maybe not when it is a multiline text, because it can extend to bottom then. When it is single-line, yes, it may look better then. So let's try, we are all CSS Gurus. We have our secret weapon, the CSS property ....

Vertical-align: middle

Add this to the style element in head ...

    .valign  {
      vertical-align: middle;
    }

... and CSS class valign to the table cells:

  <table style="vertical-align: initial;">
    <tr>
      <td>
        <div class="valign box" style="font-size: 40px;">C</div>
      </td>
      <td>
        <div class="valign box">entering not done here</div>
      </td>
      <td>
        <div class="valign box" style="font-size: 60px;">!</div>
      </td>
    </tr>
  </table>

Here is how this looks:

C
entering not done here
!

Not very much changed. What about the secret weapon :-?

So let's fix it with CSS property line-height.

Line-height: same as table row


  .lineheight  {
    line-height: 80px;
  }

Add CSS class to elements:

  <table style="vertical-align: initial;">
    <tr>
      <td>
        <div class="lineheight box" style="font-size: 40px;">C</div>
      </td>
      <td>
        <div class="lineheight box">entering not done here</div>
      </td>
      <td>
        <div class="lineheight box" style="font-size: 60px;">!</div>
      </td>
    </tr>
  </table>

Now it looks like this:

C
entering not done here
!

Ways better! Secret is to ...

Set the line-height to the same value as the row height!

DIV table

Unfortunately this behaves differently in DIV elements with display: table, also called "CSS tables". Here it is:

  <div style="display: table; ">
    <div style="display: table-row;">
      <div class="lineheight box" style="display: table-cell; font-size: 40px;">C</div>
      
      <div class="lineheight box" style="display: table-cell;">entering not done here</div>
      
      <div class="lineheight box" style="display: table-cell; font-size: 60px;">!</div>
    </div>
  </div>

And it looks like this:

C
entering not done here
!

Ooops! I'm not talking about the now missing gaps between the boxes. The text in 2nd column is dropped to baseline, and the "!" in 3rd column has lifted up. Further the whole thing seems to have a bigger height - ?

So maybe here the secret weapon vertical-align works? Lets try:

  <div style="display: table; ">
    <div style="display: table-row;">
      <div class="valign box" style="display: table-cell; font-size: 40px;">C</div>
      
      <div class="valign box" style="display: table-cell;">entering not done here</div>
      
      <div class="valign box" style="display: table-cell; font-size: 60px;">!</div>
    </div>
  </div>

Looks like this:

C
entering not done here
!


Here I stop. I don't want to dive into this. In case you have read the article I recommended at beginning, you may have noticed this sentence:

To be honest, no matter what you choose, you’ll always have trouble with inline alignments.

HTML TABLE

C
entering not done here
!
C
entered by vertical-align: middle
?
C
entered by line-height
?

CSS DIV TABLE

C
entering not done here
!
C
entered by vertical-align: middle
?
C
entered by line-height
?



Sonntag, 9. Juli 2017

CSS Ways to the Center of Middle Earth

When you are unlucky, you have to start your web developer life with a login-dialog that needs to be centered, and you are not given time to learn "on the job" how complicated this is.

Being clever, you get from the web what the web demands from you. Centering was documented compoundly by various web sites:

This Blog is about centering both horizontally and vertically.

A login-dialog must be centered both horizontally and vertically, relative to the page container. To achieve this, you need to stretch the page container to the height of the browser window. See my according Blog how to do this without side-effects:

    html, body {
      height: 100%;
      padding: 0;
      margin: 0;
      overflow: hidden;
    }

In the following I will refer to an arbitrary parent container, which also could be the body element. That means, you need the page stretching CSS above just in cases like a login-dialog, but you can apply the techniques introduced here everywhere in an HTML page.

Example HTML

Use this for experimenting on CodePen or JsFiddle.

<!DOCTYPE html>
<html>
  <head>
    <title>Centering</title>
    
    <style type="text/css">
      /* write CSS rules here */
    </style>
    
  </head>
  <body>
  
    <div style="border: 1px solid green; width: 400px; height: 200px;">
  
      <div class="center-parent" style="border: 1px solid red;">
    
        <div class="center" style="background-color: silver;">
          <p>This must be centered!</p>
        </div>
      
      </div>
    
    </div>

  </body>
</html>

In the <style> element in <head> you should paste the CSS that I will introduce in the following.

The reason why there are three levels of nested div elements is that I wanted to show that not every parent element up to root needs to carry the center-parent CSS class.

The first of the three nested div elements is a sized container, its children will be centered relatively to it. Centering needs treatment of the parent of the element to center, thus there are two more nested elements, marked with two CSS classes:

  • center-parent must be put onto the direct parent of the element to center
  • center must be put onto the element to center

Now let's implement these CSS classes.

Centering by display: flex

This is the most elegant way to do it. Drawback is that older browsers do not support it.

    .center-parent {
      display: flex;
      justify-content: center;
      align-items: center;
      
      height: 100%;
    }

Notice that the center CSS class was not used. It is not necessary in this variant. Which makes it my favorite.

The 100% height on the parent is needed, but 100% width is not needed for block elements. (Mind that there is a difference between 100% width and the default "auto" width of a block-element.)

Centering by transform: translate

Yes it's true, you need 2D instructions to achieve centering in CSS. This variant will work also on older browsers, but you may need to provide browser-specific names for the transform property.

      .center-parent  {
        height: 100%;
      }
      .center  {
        display: inline-block;
        position: relative;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
      }

In this variant you need to put the according CSS classes onto the parent and the centered element. The parent needs to have 100% height.

The element to center needs to have display: inline-block to give it its natural size, i.e. not spanning the whole width by default.

Then it must be changed to position: relative, so that you can move it. You place its top right corner to the middle of the parent, finally the transform calculates the element's dimension and moves it to where it should be.

Proof of Concept

Here comes above code in action (the transform - translate variant):

This must be centered!

There are also other ways to center, e.g. display: table on the parent with display: table-cell on the child, but this puts uncomfortable constraints onto the children of the centered element.




Samstag, 8. Juli 2017

Ubuntu LINUX Update with Untrusted Packages

There were times when a LINUX update was really risky. Nowadays, with Ubuntu, it has become quite reliable. Nevertheless I encountered strange messages on today's upgrade. U-U-U.

What Happened

I made following screenshot after the upgrade, so you don't see the many packages the updater announced. Anyway, this is the dialog you see when you launch the Ubuntu software-updater.

So, imagine there were a lot of packages in this list, almost 1 GB download volume. I clicked "Install Now" and saw this:

I saw the "Settings" button and clicked it to see what's inside, essentially to allow upgrading untrusted packages. This is the dialog that opened:

I did not find an option to allow update of untrusted packages. I could have disabled "unsupported updates", but does this include "untrusted packages"?

Anyway, I clicked "Close" without having changed anything, and the software updater opened again. I thought it would be impossible to find out the untrusted packages in 1 GB download volume, so once more I clicked "Install". This resulted in following error message:

This was the end, when I clicked "OK", the update was over, without having done anything.

How I Fixed It

Such imprecise error messages are hard to evaluate. I did not observe any network instability. When I tried again to launch the software updater, it had no problem downloading its update information. But the same problem happened again when I tried to "Install", it complained about untrusted packages.

So I started to search for untrusted candidates in that very big list, and suspected Opera and Skype.

As soon as I deactivated their update in the list, it worked!

So what? I do not understand. Why are untrusted packages in the update list when they prevent update anyway? To avoid such pitfalls in future I deactivated their update in the "Settings" dialog:

After this, when I launched the software updater, I saw following message (instead of the list of packages to update):

What to Learn

Being user-friendly seems to not always be a goal of open source software :-)

  • Software should not confront the user with unmanageable situations,
  • instead it should tell the user how to get around the problem.
  • When there is no known workaround, it should give very precise information about what happened, so that the user may find help elsewhere.
  • An "Ok" button should never terminate an application. Such would have to be labeled "Exit" or "Quit".
  • Avoid dialogs. They restrict the freedom of the user, and nobody knows where they lead to.