npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

microdom

v0.2.2

Published

A tiny dom that is not compliant with the w3c dom specifcation

Downloads

3

Readme

microdom

A tiny dom that is not compliant with the w3c dom specifcation

travis

browser support

Why?

After writing jsdom, some things have been bugging me.

  • how much of the dom do I actually need?
  • why are the methods specified in the dom so obtuse?
  • what if I want to abuse the dom and use it for storage?
  • how do I get the dom to be faster?

I'll attempt to answer these questions with this library. Sure, it's non-standard, but in the majority of cases you don't really need the rigidity that the dom spec forces you into.

So how does it work?

It's simple. There is a constructor called MicroNode which is going to be the basis of this library. MicroDom extends off of this and acts as nothing more than a node to keep track of where the root of the document is. Each MicroNode has a reference to it's parent and root, and these are mantained for you when you use the management methods specified below.

MicroNodes have node idea what a normal html element is and whether or not they are one. For many cases, it is not important. Especially in an environment where there there are no graphics being displayed or users to create input events. read: node.js or in a webworker

there is no such thing as a live nodelist *waves spoon*

These are by far the biggest performance killer of a dom, and this library sidesteps this inefficiency by (hatefully?) ignoring it.

The key to going fast is doing less.

I want to go fast too!

You can! You'll need to install this library, which is pretty simple. There are a couple ways to do this:

node

npm install microdom

browser

<script type="text/javascript" src="http://raw.github.com/tmpvar/microdom/master/microdom.min.js"></script>

Ok, I'm ready to go fast now.....

Alright, let's start off with something simple


// var microdom = require('microdom'); // node

var dom = microdom('<base href="http://url.com"/><a href="/test">tmpvar</a>');
console.log(dom.child(1).attr('href')); // /test

That was pretty easy, right? Ok, lets talk about it for a second. You may have noticed that the href attribute has not been messed with. That is by design!

Huh?

Well, the idea is that if you need to convert an href or do special processing on attributes/nodes, you should probably do that when you need it. If I tried to handle all of the cases for you, then I'd just be building jsdom again!

Put another way, if you need the href of an anchor to resolve based on the <base> element of the page or some other logic, write a module that does exactly that.

See the Extending the microdom section below

Sounds good, what methods can I use?

to create a new dom use the microdom([xml]) method which may be passed an optional xml string.

If you have an instance of a sax parser, you can pass that instead and microdom will rig up the events for you and return you the dom immediately. It will probably take a bit before the entire dom is populated so you'll want to wait for the parser stream to finish before operating on the dom!

Here's what that looks like:


var sax = require('sax');
var microdom = require('microdom');
var parser = sax.parser(true);

var dom = microdom(parser, function() {
  console.log(this.child(0).attr('class')) // 'testing'
});

parser.end('<a class="testing">blah</a>')

append

create a new node and append it to node

var anchor = node.append('a', { href : 'http://tmpvar.com' })`

append an existing node to another's children array

node.append(anotherNode);

append some xml

node.append('<a href="test">testing</a>');

In either case the return value of this function is the node that was appended

attr

Get/Set attributes on a node

node.attr('class', 'small');
console.log(node.attr('class')) // small

This will trigger a +attr.class event. Changing the class after this point generates a ~attr.class event.

Ignore attributes

You can also remove attributes by passing null as the value like so:

var node = microdom().append('a', { class: 'small' });

node.attr('class', null);

This will trigger a -attr.class event

Set a bunch of attributes at once

var node = dom.append('a');
node.attr({
  id: 'test',
  class: 'right-aligned'
});

note: this will emit two events: +attr.id and +attr.class with the appropriate values.

child

Get a child at the specified index. Providing an invalid index will result in this method returning null.

var child = node.child(0);

children

Get the array of children that have the node as their parent.

var array = node.children();

length

Get the number of children attached to this node

var length = node.length();

prepend

works similar to append but instead of putting the incoming node at the end of the children array, it will put it at the beginning

remove

remove a child by reference

node.remove(child)

remove a child at index

node.remove(0);

In either case the child that is being removed is returned to the caller

Extending the microdom

Since this is intended as a base level dom implementation, It would be rude if there were no mechanisms for extending it. The following sections detail how you would go about making microdom work for you.

Custom Constructors

The biggest issue at this point is handling special case tags as they go through their parse step. To provide a special object for tags you will want to use the microdom.tag function. It works something like this:

function Anchor() {
  this.type = "anchor";
  microdom.MicroNode.apply(this, arguments);
}

inherits(Anchor, microdom.MicroNode);

// add a click method
Anchor.prototype.click = function() {
  console.log('clicked!');
}

// Associate 'a' tags with the Anchor constructort
microdom.tag('a', Anchor);

microdom('<a />').child(0).click(); // outputs 'clicked!'

Now whenever the parser sees an a as the name of the tag, an Anchor will be created instead of the default MicroNode

Custom Functionality

You can extend the prototype of microdom.MicroNode at any point and reap the benefits immediately.

Here's an example that will add a node.getElementsByTagName function much like the the dom in your browser.

  microdom.plugin({
    getElementsByTagName : function(name) {
      
      var ret = [], c = this.children(), l = this.length();
      for (var i=0; i<l; i++) {
        Array.prototype.push.apply(ret, c[i].getElementsByTagName(name));
      }
      
      if (this.name === name) {
        ret.push(this);
      }
      
      return ret;
    }
  });

Keep up with mutation events

Instead of baking all sorts of caching behavior into microdom, there is a mutation event interface that notifies listeners whenever common things happen.

  • add node - dom.on('+node', ...)
  • remove node - dom.on('-node', ...)
  • add attribute - dom.on('+attr.<name>, ...)
  • change attribute - dom.on('~attr.<name>', ...)
  • remove attribute - dom.on('-attr.<name>', ...)

Please note that mutation events are only emitted from the root node. The bubble/capture event system should exist in userland.

Here's how you would listen for updates to any class attribute in the dom:


var dom = microdom();
var node = dom.append('a');

dom.on('~attr.class', function(node, attributeValue) {
  console.log(node.name + "'s class is now", attributeValue);
});

node.attr('class', 'biglink'); // outputs "a's class is now biglink"

Mutation events are prefixed to separate them from other types of events

  • ~ - change
  • + - addition
  • - - removal

for more info see the mutation events section in test/test.js

Finding plugins

Easy peasy, hit up this url http://npmsearch.com/?q=keywords:microdom,plugin

License

MIT (see license.txt)