Monday, August 5, 2013

building custom tags in html5 and javascript, part 2

Several months ago, I toyed around with the idea of building custom, JavaScript powered tags. My motivation for this was simple: It makes building the client-side portion of web application easy. But, it only handles one side of the equation elegantly: binding existing markup to JavaScript objects.

Ideally, I'd like to work the other way around too. I'd like to be able to create a new object and have the corresponding markup generated for me. And I'd like to drop the object right into the DOM without performing any object-to-node conversion.

We can start with a markup template. And we can plan on consuming the data-id attribute to identify nodes within the object [to avoid conflict with any existing id's in the DOM]. So, let's consider a simple "Blurb" ... like so:

<div class='blurb'>
  <h3 data-id="blurbTitle" style='text-decoration: italic;'>Title will go here</h3>
  <p data-id="blurbBody" style='color: silver;'></p>
</div>


And we want a JavaScript "class" definition that shows and hides the body when on click:

var Blurb = function(title, body) {

  this.init = function(title, body) {
    this.setTitle(title);
    this.setBody(body);
  }

  this.setTitle = function(t) {
    this.blurbTitle.innerHTML = t;
  }

  this.setBody = function(b) {
    this.blurbBody.innerHTML = b;
  }

  this.onclick = function() {
    if (this.blurbBody.style.display == 'none') {
      this.blurbBody.style.display = 'block';
    } else {
      this.blurbBody.style.display = 'none';
    }
  }


  this.init(title, body);

}


Using that template and class, we want to be able to throw a Blurb on the page by doing this:

var b = New(Blurb);
b.blurbTitle.innerHTML = "Some Title";
b.body.innerHTML = "Some body text";
document.body.appendChild(b);


Or this:

var b = New(Blurb);
b.setTitle("Some Title");
b.setBody("Some body text");
document.body.appendChild(b);


Or this:

var b = New(Blurb, "Some Title", "Some body text.");
document.body.appendChild(b);


All we're missing is a simple, easy-to-understand link between the template and the class definition. We need a call to something to make the Blurb constructor operate on a node based on the markup from the template, with the title and body nodes imported into the object as properties. (The outer div will be this.)

Sounds easy enough. But, it's a two-step process. Firstly, you'll notice I used a New() function instead of the new keyword, and that's for good reason. The new keyword will create a new this, which won't be our DOM node. And it's pretty tricky, if not impossible, to "consecrate" an existing object as a DOM node. So, by making a simple concession to use a New() function instead we make our job a lot easier.

So, let's write the fundamentals. Let's write a method that applies our Blurb constructor to a DOM node based on our template. (For now, let's assume we're attaching our template markup as text to class definitions themselves. I.e., Blurb.templateMarkup = "<div ... ")

function New(DomClass) {
  // create a container for the template markup
  var rv = document.createElement('div');

  // drop the markup in and grab the first child, which is what we're really after
  rv.innerHTML = DomClass.templateMarkup;
  rv = rv.firstChild;

  // and "bless" the return value with the class constructor, supplying any ~additional~
  // arguments that were supplied to this New() call
  DomClass.apply(rv, Array.prototype.slice.call(arguments, 1));

  return rv;
}


Simple enough. That gets us a DOM node that's "blessed" as an instance of the supplied object. The missing link: import data-id's from the template as object properties. And for now, we'll assume this must be done "pre-blessing" in simple, recursive manner. So, let's create a little helper function to do that work, which we can define and call in the context of New().

function New(DomClass) {
  // create and bless the object as before
  var rv = document.createElement('div');
  rv.innerHTML = DomClass.templateMarkup;
  rv = rv.firstChild;

  // before blessing it, import the properties
  importProperties(rv, rv.childNodes);

  DomClass.apply(rv, Array.prototype.slice.call(arguments, 1));
  return rv;


  // hoisted functions:

  // given a list of nodes, recursively attaches any with data-id's as properties of obj
  function importProperties(obj, nodes) {
    for (var i = 0; i < nodes.length; i++) {
      if (!nodes[i].getAttribute) continue;
      var id = nodes[i].getAttribute('data-id');
      if (id) {
        obj[id] = nodes[i];
        importProperties(obj, nodes[i].childNodes);
      }
    }
  }

}


And we're done. Our desired calls ...

var b = New(Blurb, "Some Title", "Some body text.");
document.body.appendChild(b);


... (etc.) will work as expected now. Give it a try.

Next time, we'll use what we've done here to improve our work from part 1 and bridge any gaps between the two. Hopefully, we'll end up with a robust system for more seamless, elegant, and straightforward interactions between our script and the DOM.

17 comments:

  1. I found your blog while searching for the updates, I am happy to be here. Very useful content and also easily understandable providing.. Believe me I did wrote an post about tutorials for beginners with reference of your blog.
    Sql server dba online training

    ReplyDelete
  2. Good Post! Thank you so much for sharing this pretty post, it was so good to read and useful to improve my knowledge as updated one, keep blogging.
    Selenium training in Chennai || Devops online training

    ReplyDelete
  3. Thank you for allowing me to read it, welcome to the next in a recent article. And thanks for sharing the nice article, keep posting or updating news article.
    I think you have a long story to share and i am glad after long time finally you cam and shared your experience.
    Docker online training
    Docker certification training
    Docker online course
    Docker training course

    ReplyDelete
  4. Exactly! This is a great information on custom tags in html5.
    Twin Cities Website Design

    ReplyDelete
  5. Very excellent post!!! Thank you so much for your great content. Keep posting.....

    Spark and Scala Training
    Apache Spark Online Course

    ReplyDelete
  6. I am really happy with your blog because your article is very unique and powerful for new.
    Data Science Course in Pune

    ReplyDelete
  7. It's really a cool and useful piece of information. I satisfied that you shared this useful information with us. ufabet1688

    ReplyDelete
  8. Sharma Academy is Central Indias largest provider of Mppsc Notes and Mppsc Study Material. You will get updated MPPSC Notes as per the latest syllabus of state level psc exam in Hindi and English medium both.

    ReplyDelete
  9. Some programs cater more towards the "managerial" side of cybersecurity, whereas some other programs, like the certificate track at Stanford University, Internet of Things in cybersecurity

    ReplyDelete
  10. Kya aap bhi apna partner ko stasify nhi kare paate hain toh aaj mein aapko time badhane wale condom kaun se hain unke baare mein btaunga jiske istmaal se kaffi lambe time se aap apni sex karne ki timing increase kare sakte hain

    ReplyDelete
  11. Thanks for sharing such informative guide. This post gives me detailed information. I am working as trainer in leading IT training academy offering Linux Training Course in Delhiand i use your guide to educate my students

    ReplyDelete