DEV Community

loading...
Cover image for Introduction to the Document Makeup Library (DML)

Introduction to the Document Makeup Library (DML)

Eckehard
Inventor. Always interested in new experiences
Updated on ・3 min read

The web has evolved a lot since the days of Tim Berners-Lee, but we are still using some of the initial concepts. From a technical view, HTML is superfuous: We can access the HTML-DOM-API directly to build the DOM. From a programmers view, there are good reasons to rethink the whole concept.

The Document Makeup Library (DML) is a new Javascript library that has just been released as Open Source. DML makes it increadibly easy to build complex web applications using just vanilla javascript (one language to rule them all...). It let´s you create DOM elements using the tags you are used to - not in html, but from within Javascript. h1() creates an <h1>-element, p() a <p> and so on. This makes it possible to "bypass" the traditional HTML-approach completely.

What´s the advantage? Much more than you would think:

  1. Element creation gains some programmatical "intelligence": You can create new elements on the fly using conditions If -> create element A, else -> create element B.
  2. Most Elements have been "pimped" a bit to be more accessible: Simply apply an array to a list using ul(["Milk","Butter","Tea"]) to create a populated list.
  3. DML functions return references to DOM elements, so there is no need to use "getElementById" anymore. It is even not neccessary to use ID´s and Classes at all (though you can still use them if you want). Because elements are created from within Javascript, DOM elements become part of Javascript objects giving direct access to the object state.
  4. No need to use a virtual DOM, direct access is blazing fast on current browswers.
  5. No need to use a Shadow DOM, as CSS properties can be fully encapsulated to prevent side effects.

You will find more useful aspects using the DML-approach. After a few days you will ask yourself how you did it before?

Scope of use

  • Build complete web applications with DML from sratch (Which is prooved by the DML homepage and other applications). This is easier if you have some experience to use object oriented design patterns.
  • Use Javascript functions and classes instead of templates and components. It is easy to create your own module libraries that provide reusable components
  • DML also integrates seamlessly with other frameworks, as it does not presuppose a certain workflow.
  • If you like them: DML is ideal to create webcomponents
  • DML can also integrate existing webcomponent libraries into your workflow. Just write some short wrappers.

The core of the DML-libary is free (Open Source under the CC0-1.0 License), a complete introduction and reference can be found here. There are some more advanced modules available for professional use (e.g a charting library with an unlimited number of axis on each side of a diagram).

The homepage itself was created using DML too. It provides lot´s of "living" examples that can be changed to immediately see the effects. Please try out yourself!

What about SEO?

It is true: DML creates dynamic websites, bypassing the traditional HTML-part (though you can still use HTML). Some search engines will not be able to read the content of your site then (beside Google, that can render and index dynamic pages too). But this is not different from React or Vue, that have a similar approach. DML is still a very young project, so server side rendering is not yet implemented.

Discussion (15)

Collapse
efpage profile image
Eckehard Author

List of examples (to be continued)...

There are some new examples to show, how short things can be with DML:

clocks

Collapse
artydev profile image
artydev • Edited

TinyCounter :

import {button, idiv} from "./dml"; 

function Counter () {
    let count = 0;
    let value = idiv("0");
    let binc = button("inc");
    let bdec = button("dec");
    binc.onclick = () => value.innerText = ++count;
    bdec.onclick = () => value.innerText = --count;
}

Counter();
Enter fullscreen mode Exit fullscreen mode

ToDo (Official) : Todo (100% lighthouse :-))

ListUpdated : ListUpdated

Counters : Counters

Simple TopBar : TopBar

Simple List demo : SimpleList

Binary is live here Binary

Clock is live here Clocks

Eckehard, I can't make webcomponents work, could you show an example by completing one of your script ?

Regards

Collapse
efpage profile image
Eckehard Author

Hy artydev,

very nice you put the examples online, thank you much for that!!!

As mentioned, the DML-class was not intended to be used as a webcomponent, so I did not check out how to pass parameters to WC. There are also some strange effects which may require some investigation. As you may see, the clocks are not inside the div.

clocks
Here is the index.html:

!DOCTYPE html>
<html lang="de">

<head>
  <meta charset="utf-8">
  <title>title</title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <script src="https://efpage.de/DML/DML_homepage/lib/DML-min.js"></script>
  <script src="myclock.js"></script>
</head>

<body>
 <h2>Clock around the clock tonight...</h2>

  <div style="border: 2px solid black; padding: 10px; display: box;">
    test
    <my-clock></my-clock>
    <my-clock></my-clock>
    <my-clock></my-clock>
  </div>
</body>

</html>
Enter fullscreen mode Exit fullscreen mode

and myclock.js:

// Class wrapper function - not necesary for WebComponents
function myClock(size = 300, attrib) { return new _myClock(size, attrib) }

// The clock class
class _myClock extends HTMLElement {
  constructor(size=100, attrib) {
    super()
    this.size = size
    const cx = size/2, cy = size/2;  // Radius
    const _clockstyle = "width: " + (size) + "px;  height: " + (size) + "px;"
      + "border: 7px solid #282828; background: #585858;"
      + "border-radius: 50%; margin: 10px;"
      + "box-shadow: -4px -4px 10px rgba(67,67,67,0.5), inset 4px 4px 10px rgba(0,0,0,0.5),"
      + "inset -4px -4px 10px rgba(67,67,67,0.5), 4px 4px 10px rgba(0,0,0,0.3);"

    this.base = sidiv("", _clockstyle+attrib)
    let c = this.canvas = canvas2D({ width: px(2 * cx), height: px(2 * cy) })

    c.ctx.lineCap = "round"
    unselectBase()

    // Paint anything radial
    function tick(color, width, angle, length, innerlength = 0) {
      function ls(length) { return length * Math.sin(angle / 180.0 * Math.PI) }
      function lc(length) { return -length * Math.cos(angle / 180.0 * Math.PI) }
      c.setLineType(width, color)
      c.line(cx + ls(innerlength), cy + lc(innerlength), cx + ls(length), cy + lc(length))
    }

    // Draw clock
    function drawClock() {
      c.clear()
      // Draw ticks
      for (let i = 0; i < 360; i += 30)
        if ((i % 90) == 0) tick("#1df52f", 5, i, size*0.45, size*0.40)
        else tick("#bdbdcb", 3, i,  size*0.45, size*0.43)

      // draw hands
      let t = new Date();  // get time
      tick("#61afff", 5, t.getHours() * 30, size*0.25)  // hour
      tick("#71afff", 2, t.getMinutes() * 6, size*0.35)  // min
      tick("#ee791a", 2, t.getSeconds() * 6, size*0.42)  // s

      // drwa center
      c.setFillStyle("#4d4b63")
      c.circle(cx, cy, 10, { fill: true })
    }
    drawClock()
    setInterval(drawClock, 1000)
  }
}

window.customElements.define('my-clock', _myClock);

Enter fullscreen mode Exit fullscreen mode
Thread Thread
artydev profile image
artydev

Thank you Eckehard

Here is the live version ClockWC

Collapse
artydev profile image
artydev • Edited

Hy Eckehard,

I would like to import dml.js in a main app.js
In order to tree shake the bundle.
I encountered some errors like : 'function p' already declared.
Is this in your project ?
Regards

Collapse
efpage profile image
Eckehard Author

Hy,
yes, p() is one of my functions. I should think about building a new class where all this functions are part of like dml.p(), dml.print to avoid naming conflicts. Or supply dml as a module, which will have the same effect.

DML currently is a script without import/export, which is really a shortcoming. Thank your for the hint, maybe I find some smart solution for that....
Regards

Collapse
artydev profile image
artydev

Thank you Eckhard,
I really believe in DML ant its potential :-)

Thread Thread
efpage profile image
Eckehard Author

Yes, first commercial projects we have done using DML have been very promising.

But there is one issue I could not solve til now:

Using scripts instead of ES-modules is quite convenient, as it let´s you use all your functions without effort. BUT: it adds all defintions to the global namespace, which earlier or later will result in name clashes. Additionally, DML currently contains lot´s of constants and functions just for convenience, giving a total of about 170 names in your namespace, and most of them are rarly ever used.

There are several solutions discussed here using ES-modules, which is the right way to do, but has drawbacks too:

  • Using named input with a list of 170 in every module is not very convenient. Maye, we can organize our import in functional groups like this:
    import {selectBase,  unselectBase, setAttributes, make, appendChilds} from "./dml.js"; // base routines
    import {h1, h2, h3, h4, h5, h6} from "./dml.js"; // page elements
    ...
Enter fullscreen mode Exit fullscreen mode

Don´t know if this has some negaive impact?!?

  • Using wildcard import makes us use the alias on every identifier, which may sum up to some thousand alias in every project. This is boring and reduces the readability of the code.
  • We could put the whole script into a function and use the "revealing pattern" to isolate the namespace, which in fact has the same result as a wildcard import from an ES-module that we need the function name as an alias
  • we can use a mixed pattern using named import, but put things together like this:
export const css = {
    bold : "font-weight: bold;";
    italic : "font-style: italic;";
    bigtext : "font-size: 130%;";
    bg : "background-color: ";
    bgred : "background-color: red;";
    bgred2 : "background-color: #f50;";
    bgy : "background-color: #ffc;";
    bggreen : "background-color: #695;";
        ...
}
Enter fullscreen mode Exit fullscreen mode

Here, all constants are grouped in JSON-object "css", which can be import as an entity. Now we use div("",css.bold) instead of div("",_bold), which is not really bad, but does not really improve the readability. As it makes the intention clearer, maybe this could be an advantage.

  • We could split the core script into functional parts like:

DML_core.js
DML_util.js
DML_consts
DML_...

which would allow more fine grained import, but I assume, most of the time we would import all modules anyway.

Is there a better solution?

I would really appreciate if there was a way to use the library inside a scoped context, so all definitions live only inside this context, similar to the revealing pattern.

We CAN implement such a pattern using classes:

class DML {
        privateVar = "Ben Cherry",
        publicVar = "Hey there!";

        privateFunction() {
            console.log( "Name:" + privateVar );
        }

        publicSetName( strName ) {
            privateVar = strName;
        }

        publicGetName() {
            privateFunction();
        }
    }
export { DML }

---------
class myNewApp extends DML {
      main(){
               this.setName( "Paul Kinlan" );
               ...
     }
}
Enter fullscreen mode Exit fullscreen mode

This would be a nice approach, if it was not Javascript we where using. Here we need to write this as a prefix, which is not any better.

Using named inport currently seems to be the most appealing and correct way, but it seems to have very little advantage over the current "script"-approach (beside the possibility to avoid name clashes). If there is any better solution, any suggestion is very welcome.

Thread Thread
artydev profile image
artydev

Hy Eckehard,
I also believe named import is the best solution, and let tools like Rollup do tree shaking for production.

But, I also think you could, at the same time deliver DML_core.js (and co.) which would benefit those who want don't want to deal with node.

Regards

Collapse
artydev profile image
artydev

Hy Eckhard

Thank you for your work.
Waiting for more posts :-)
Regards

Collapse
efpage profile image
Eckehard Author

The library is under constant development, but the basic paradigm and routines are fairly stable now. So I would be happy to see, what others can do with it. For my own projects it is a real timesaver and I cannot even think to use HTML the "traditional way" anymore.

DML itself is released under an open source license, but it is intended to allow also commercial development of new components, that are not necessarily free. For now I would be happy to find people to contribute to the project (or even help to find bugs or make things better).

As I have demonstrated in my last post, that it is very easy to create useful new elements or own Webcomponents.

Please check out the documentation with lots of "living" examples. More demonstations are about to come...

Collapse
artydev profile image
artydev

Thank you Eckehard, I will investigate more :-)

Collapse
jankapunkt profile image
Jan Küster

I think the need for static HTML is overrated. In 2021 it's totally okay to have a Website being dynamically rendered.

Collapse
simpleadam profile image
simpleAdam • Edited

do you have a vision for how this would be used, or I mean what sort of problem it was created to solve

Collapse
efpage profile image
Eckehard Author • Edited

Well,

DML was created to write complex web applications. Not all sources have been published yet, but the basic modules are open source now. As you may have seen from the examples, DML makes it very easy to create object oriented web modules. We found the we got less problems with DML than with WebComponents and things grow very fast.

DML was inspired by the concept of Delphi components. As far as I can see, it works pretty well. We will present more modules in the near future to let you estimate the real power of this concept.

Some more details about functional templates are given here. DML enables the use of OO-concepts for web design, which is important to handle larger applications. I will demonstrate the use of classes in a later post.