Giter Site home page Giter Site logo

better-dom's Introduction

better-dom: Live extension playground
NPM version Build Status Coverage Status NPM downloads Twitter

API DOCUMENTATION

This library is about ideas. After some time of using jQuery I found that it's just too big, has lack of features needed and the API design is debatable. In particular live extensions was one of the main ideas that encouraged me to build a new library from scratch.

Vanilla DOM also has a lot of bad parts, that I'm trying to fix by providing a JavaScript wrapper for each DOM element you use in code. This extra layer allows to abstract from legacy interfaces and to add new methods on the top of particular elements without touching vanilla DOM prototypes. So the object model used is very different from what jQuery does.

Note, that the better-dom project is only about the DOM. It does not contain any AJAX or BOM helper.

Sauce Test Status

Features

Installation

$ npm install better-dom 

Then just include the script below on your web page:

<script src="node_modules/better-dom/dist/better-dom.js"></script>

Documentation

Contributing

In order to modify the source code you have to install gulp globally:

$ npm install -g gulp

The project uses set of ES6 transpilers to compile the output file. You can use command below to start development:

$ npm start

After any change it recompiles build/better-dom.js and runs unit tests automatically.

Browser support

Desktop

  • Chrome
  • Firefox
  • Opera
  • Safari
  • Edge
  • Internet Explorer 10-11

Mobile

  • iOS Safari 7+
  • Chrome for Android 30+

better-dom's People

Contributors

chemerisuk avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

better-dom's Issues

import... from.... ?

I am entirely unfamiliar with this syntax.

import _ from "./utils";
import DOM from "./dom";

From where is it derived, may I ask?

A Node was inserted somewhere it doesn't belong.

There is a bug in dom.importstyles.js @line four:
https://github.com/chemerisuk/better-dom/blob/master/src/dom.importstyles.js#L4

If you have some HTML comment between the HTML and HEAD tags, _.docEl.firstChild refers to such a comment and thus _.docEl.firstChild.appendChild() throws the error mentioned in the headline. Instead of firstChild you should use something like _.docEl.querySelector( 'head' ) to properly retrieve the HEAD element.

The markup to reproduce that error: (firstChild refers to <!--<![endif]-->)

<!DOCTYPE html>
<!--[if lte IE 8]><html class="no-js lt-ie9"><![endif]-->
<!--[if gt IE 8]><!--><html class="no-js"><!--<![endif]-->
    <head>
    </head>
    <body>
    </body>
</html>

Sometimes collections of element have empty methods

DOM.find("#groups").findAll(".group-nav").length
// 3
DOM.find("#groups").findAll(".group-nav").hide
// function () {}

show() worked, then hide & show methods were both empty. It worked with each().

Seen on Ti build on ipad. I'll report later if it happens on regular mobile safari

Shadow DOM support

Version 2.1.0 will introduce new method $Element#context. The goal for this method is to create new browser contexts, e.g. independent subtrees in the DOM. Those subtrees provide:

  1. scoped CSS rules: you always start with default UA rules
  2. internal DOM events do not bubble into the document tree
  3. subtree is not accessible via find[All] (neither native querySelector[All]) because it's in another document.
  4. elements inside of context can't be crawled, therefore they are not SEO-frieldly
  5. potential support for element media queries in future (after implementing #23)

However contexts are not equal to the Shadow DOM - they introduce several extra elements in the main document. But the big advantage is that we can use them today (yes, IE8+ is supported!).

nasty error message in manipulation module

I was reading the documentation about the append(). It stands it can handle array ( [] ). So i tried this:

link.append( [elem, elem] );

and

link.append( [string, string] );

If you try, you will see it throws a nasty message. I figured out you was meaning

link.append( elem, elem );

but for me this is not an array.

I'm writing fast on my mobile now, so I know bad format on this issue.

Getting event info from a keypress event

As I understand it, better-dom does not pass the event object to an event handler.

It isn't clear to me how I can retrieve information about the event that triggered a specific event.

For example:

DOM.find('body').on('keypress', function(target, cancelled) {
  var key = ?????;
  alert("You pressed the " + key + " key");
});

how to get window and document object?

Hi

I just tested. With find() and findAll() isn't there any option to get the window and document object? The arguments only take string. So how to attach a event listener to a window object or document object?

Firefox TypeError

I get two errors in Firefox:

TypeError: Argument 2 of EventTarget.addEventListener does not implement interface EventListener.
TypeError: DOM.dispatch is not a function

Do you have any idea what might be the cause?

data-* attributes with "=" sign evaluate as objects

When specifying a data attribute which is a string containing an "=" sign, the string gets evaluates as an object. For example, the following data attribute:

data-href="/locations/suggest?query=:query"

evaluates to

Object {/locations/suggest?query: ":query"} 

I think the object notation syntax for data attributes should be more precise to avoid confusion with strings like the above?

DOM.format improvements

DOM.format can be used to construct HTML. Thats why having {...} that does not encode input values is not the best solution. Therefore need to add support for both safe and unsafe (it can also be useful) injections. Mustache has a good syntax for that familiar to any front-end developer:

{{encoded}} (by default)
{{{unencoded}}}

It would be nice to add nested object support as well:

{{some.nested.object.value}}

Because DOM.format is going to be executed in JavaScript code, there is no need to add logical operand support. They all can be covered by functors:

var varMap = {
  a: "2",
  b: "2",
  result: function() {
    return this.a + this.b;
  };
};

DOM.format("{{a}}+{{b}}={{result}}", varMap);
// => 2+2=4

Method $Node#data is deprecated in 1.7.3

Writing a code for the DOM often requires to have an extra data associated with an element instance. Therefore better-dom has $Node.prototype.data method which provides access to a private key/value map to associate any kind of object with an $Element.

var body = DOM.find("body");

body.data("key1", {a: 1, b: 2});
body.data("key2", 1985);
...
body.data("key1"); // => {a: 1, b: 2}
body.data("key2"); // => 1985
...
body.data("key1", null);
body.data("key1"); // => null

The problem

This method has a useful ability to grab and parse data from data-* attributes that have name equal to key in the private key-value map object. For example if we have an element like below:

<div id="foo" data-bar="welcome" data-obj='{"x":1;"y":"2"}'></div>

The private key-value map of the element will be automatically populated when you call the data method with appropriate keys at the first time:

var foo = DOM.find("#foo");

foo.data("bar"); // => string "welcome"
foo.data("obj"); // => object {"x":1;"y":"2"}

On the one hand this feature is very useful, because saves lines of code. On the other hand it introduces a naming collision with the get and set methods:

foo.get("data-bar"); // => string "welcome"
foo.get("data-obj"); // => string '{"x":1;"y":"2"}'

Having both foo.data("bar") and foo.get("data-bar") to do similar thing looks confusing.

Conflict with the HTMLElement.dataset

The data method behaviour looks similar to what the HTML5 dataset attribute does. But the dataset attribute has several important differences:

  1. it supports only string values
  2. it updates data-* attribute value when appropriate property was changed (that makes it very slow comparing to regular JavaScript objects BTW).

So data and dataset have very different behaviour despite on similar names. This introduces a small logical conflict with existing standards.

The sense and goals of the $Node.prototype.data method is very close to storing private data associated with an $Element instance. Thats why I've made the phrase private key-value map to be bold several times.

Solution

In JavaScript private properties are usually prefixed with the underscore symbol. Why not to use this syntax we are all familiar with that also explains the sense of the method clearer than the data word:

foo.get("_bar"); // => string "welcome"
foo.get("_obj"); // => object {"x":1;"y":"2"}

It looks very clear for me and takes the same number of symbols and a line with data word. So in theory you should write programs with the same speed :)

Another advantage of this change is that it allows to reduce amount of code, because the data method has similar code parts with getter and setter. Also, the new watch method can reuse some logic to listen to changes of the private key/value map as well.

The change for getter and setter submitted for the 1.7.3, because it won't break anything. $Node.prototype.data will be removed in 1.8, the next minor release.

better-dom.js: undefined is not a function

Recieving this error from better-dom script when trying to get timeinput polyfill to work:

Uncaught TypeError: undefined is not a function    better-dom.js:90

Code corresponds to this line:

        safeInvoke: function(context, fn, arg1, arg2)  {
            if (typeof fn === "string") fn = context[fn];
            try {
                return fn.call(context, arg1, arg2);
            } catch (err) {
   -->          WINDOW.setTimeout(function()  { throw err }, 1);
                return false;
            }
        },

Not sure if this issue should be filed here or with the polyfill. Assumed here as it's an error thrown from better-dom.js

Thoughts on the $Element#style method

Currently there is a style method to access CSS props:

el.style("color"); // => "#FF0000"

el.style(["background-color", "font-size"]); 
// => {"background-color": "#FFFFFF", "font-size": "16px"}

and setter:

el.style("color", "blue");
el.style({"font-size": "14px", "background-color": "#FF00FF"});

I noticed that there is no intersection of DOM element and style properties (at least for now). So potentially we can safely write:

el.get("color");
el.get(["background-color", "font-size"]);

el.set("color", "blue");
el.set({"font-size": "14px", "background-color": "#FF00FF"});

In case when such assumption will fail in future, we can just use legacy method to access required value:

el.legacy(function(node) {
    node["someproperty"]; // => returns property value
    node.getAttribute("someproperty"); // => returns attribute value
    node.style["someproperty"]; // => returns style value
});

Live extensions breaks in legacy IE

Error message: Unable to get property 'split' of undefined or null reference
Line: 1686.

This happen because there is no transition or animation support in IE9.

Tried a workaround, but then better DOM throws new errors.

Line 1669. Return null.

Error now occur on line 84, and screaming "Syntax error"

I worked around this in IE Developer Tools when I started the script again. It was then working, but only toggling possible on the first buttons.

The .HTC file are not helping here either.

I modified the code a little more, so it do the mutations when I click on that button. But it will not toggle when I click on the newly created buttons.

This is working perfectly in all other browsers and IE10 and newer. But not in IE8 and IE9.

Seems to me that both the visibility methods get broken, and live extension.
It also seems not to import any CSS style rules in the live extension.

Strange is it that the non-duplicate button have toggle and can do toggle without a issue. It's only the duplicated buttons that you can't toggle.

I used IE11 in IE9 compatibility mode when I got this errors.

Code that I used to get the errors:

<button class="clazz" >Toggle</button> 
<button id="duplicate"> Duplicate</button> 
<script type="text/javascript">
DOM.extend(".clazz", { 
  constructor: function() {
        this.on('click', function() {
           DOM.find('.modal-dlg').toggle(function(target) {
            target.css('backgroundColor', 'green');
          });
   });
}
});
DOM.extend(".modal-dlg", {
  constructor: function() {
  },
    demo: function() {
       DOM.find('body').append(DOM.create('<button class="clazz"> Demo!!</button>'));
    }
});
    var signinForm = DOM.find(".modal-dlg");
  DOM.find("#duplicate").on("click", function() { 
  signinForm.demo(); // => shows the signin dialog
});

</script>

Experience a lot of issues / strange behaviour

Hi

Create script, and works for me most of the time, BUT there are issues and flaws here.

  1. mouseenter and mouseleave are not working at all
  2. pointerover and pointerout suffer the same thing as mentioned above. Not working
  3. click can be done in delegated events on disabled elements
  4. No namespaces? Any plans to add this?
  5. Your RAF solution have issues on disabled tabs when you swap browser window, are eating battery
    on mobile devices. Take a look at velocity.js to understand better: https://github.com/julianshapiro/velocity/blob/master/velocity.js#L3299
  6. Checkboxes loose their state in IE after appending elements to DOM
  7. CSS browser issues are not taken care of. Not even cross-browser.
    Example Chrome 31-36 return text-decoration-line and text-decoration-color
    which are not expected yet. See https://code.google.com/p/chromium/issues/detail?id=342126

There are other flaws too.
8. No plans to add feature for different units? Just now I see you are using only 'px'. What is someone want to use 'em' or '%'?

The list are longer, but any plans to fix this?

better-dom 4 roadmap

  • decrease number of internal objects created
  • $Element#get/$Element#set support innerHTML shortcut
  • drop $Element#data
  • drop $Element#toggle
  • drop $Element#define
  • functor as a returned value for $Element#on, drop $Element#off
  • drop support for IE8-9
  • drop support for legacy Opera
  • $Element#on/$Element#once support options object argument (capture, passive flags)
  • align live extension descriptor with the custom element spec
  • add destructor for a live extension
  • store API documentation on Wiki

Better API documentation

Are you going on improve an API documentation? It is clear in common but I haven't fully understand a DOM.extend. So, selector is fully clear, but what is condition and mixins. And I noticed a constructor it seems like triggered when node is inserted into the DOM. Isnt it? And can I apply a destructor there to rollback states of some elements for example?

DOM.ready is deprecated

After some time of using the concept of live extensions I realised that existing well-known ready method is useless. Starting from 1.7.6 the DOM.ready method is deprecated, and it will be removed in the 1.8 release.

In this issue I'll try to explain the reasons of such decision.

What the ready method does

jQuery made the concept of the ready method to be popular. But notice: there is no such method in the standard. W3C document contains only the DOMContentLoaded event, that is fired as soon as browser parses body content on the current web page.

But an important feature of this event is that the event handler is not triggered if you register a it after browser parsers the document tree. To solve this limitation we have ready method, that triggers the callback as soon as DOMContentLoaded event is fired or if it was already triggerend.

As I said before there is no ready method in the existing version of the W3C standard.

Why ready method was useful

The main goal for the ready method is to garantie that the code inside of a callback handler will be executed when your document is ready. It allows to do safe DOM querying.

<!DOCTYPE html>
<html>
<head>
    <script>
    DOM.ready(function() {
        DOM.find("button").on("click", function() {
            alert("it works from <head>!");
        });
    });
    </script>
</head>
<body>
    <button>test</button>
    <script>
    DOM.ready(function() {
        DOM.find("button").on("click", function() {
            alert("it works from <body> as well!");
        });
    });
    </script>
</body>
</html>

Inside of the callback people usually do some initialization, DOM mutations, attach event handlers etc. This is kind of safe start point for any JavaScript for DOM.

Live extensions make the ready method less useful

The main goal for live extensions is to fill gap of the ready method that doesn't work for future HTML content inserted via JavaScript. Additionally to that there are several issues that I think makes sense to cover to clarify the decision of deprecating the DOM.ready in favor of DOM.extend.

Issue 1: code separation

If you use JavaScript a lot on client side, you often need to initialize bunch of plugins. After some time your code looks like below:

DOM.ready(function() {
    // init plugin 1
    // init plugin 2
    ...
    // init plugin n
});

The ready method callback quickly becomes to be a huge. It's possible to split it into several calls and move into several separate files but it's a rare practice.

The main reason of such situation is that every ready call is very similar. There is no distinction and separation between them by sense.

This is what makes live extensions to be different. The first argument of the DOM.extend call is a CSS selector that usually describes the sense of the extension (based on class or tag name). So it means you are forced to separate your code by sense that in general is a good practice to follow.

To summarize: using live extensions makes your code to be more maintainable.

Issue 2: testability

In JavaScript functions produce a new context, and all local variables are not accessable outside. Because of that ready methods are usually hard to test or they require additional refactoring.

DOM.ready(function() {
    // how to test logic here?
});

During development of the live extensions concept I kept in mind testability. And existing API allows to test constructors very easily.

This is how a live extension looks like:

DOM.extend("selector", {
    constructor: function() {
        // extension initialization
        this.doPartOneOfInit();
        ...
        this.doPartTwoOfInit();
    },
    doPartOneOfInit: function(a, b) {
        // do part one
    },
    doPartTwoOfInit: function(c) {
        // do part two
    }
})

Then you can make a mock object that have private methods on it:

var mockObject = DOM.mock("selector");

mockObject.doPartOneOfInit(1, 2);
// assert state
mockObject.doPartTwoOfInit(3);
// assert state

Issue 3: doesn't work for future content

This why live extensions were created. I want to get rid of the ready calls because they have this limitation that doesn't make sense to do in most of use cases. The better-dom library APIs should ecourage developers to use a good practices, like making plugins that work initially for future content even in case they are not aware of such feature.

Conclusion

I hope the article helps to understand why DOM.ready is useless. In case you still need it you can just use the construction below:

DOM.extend("body", {
    constructor: function() {
        // this is equal to DOM.ready callback 
    }
});

Starting from 1.7.6 the method DOM.ready is deprecated. It is scheduled for removal in version 1.8.

Get all data associated with element

Hi

How do you get all data properties from an element?

With jQuery you could do this:

$('.element').data();

And you would get an array with all data.

DOM.extend no longer works for ajaxified content.

It seems that with version 1.6.0 extending elements no longer works for elements inserted by ajaxify calls, which we are heavily reliant on with the app we're building. I can't get any extensions to work anymore.

To reproduce - try extending an element and inject it via ajaxify. Result: extension is ignored, whereas the expected result is that it should be mixed in to injected element object.

browser / qsa issues

I notice that you are supporting a lot of browsers:

  • Chrome
  • Safari 6.0+
  • Firefox 16+
  • Internet Explorer 8+ (see notes)
  • Opera 12.10+

and Android 2.3

But what I can't figure out is how your find / findAll method are going to work on some of this browsers. Look at this link: http://caniuse.com/#feat=queryselector

QSA are not supported in Android 2.3, but your find method are only using QSA as far as I see.

The same for some of the other browsers better-dom are supporting.

What is the work-a-round for this things?

There are also a whole bunch of QSA bugs related to older browsers, not taken care of. But I assume that is swallowed inside your try / catch

But there are a few other places you are using QuerySelectorAll without wrapping it in a try / catch, so I guess that will throw error in older browsers?

And how is your extension function going to work without QSA for older browsers that you are supporting??

Example:

  util$index$$default.each.call(DOCUMENT.querySelectorAll(selector), ext);
        // MUST be after querySelectorAll because of legacy IEs quirks
           DOM.importStyles(selector, global$extend$$cssText);

How is this code going to be supported in Android 2.3 and other browsers that don't have QSA?

There are also issues with matchesSelector the same way. Look here:
http://caniuse.com/#feat=matchesselector

As a workaround here, you are using qsa, but what you do for the browsers that don't support QSA?? Then matchesSelector will break, or I'm wrong here??

Ajax in better-dom

Hi mate . i'm new to javascript and your awesome work the "better-dom" and im just started working with ajax , i saw your better-xhr project and know its still in development , i also know your better-ajaxify isn't supporting post request so any idea how to use properly all of ajax features in better-dom ?

Cannot read property 'getComputedStyle' of null

When I hide the DIV that contains (better-dateinput-polyfill) and later when I show it and click on the input [type=date] polyfill causes the following error.

Uncaught TypeError: Cannot read property 'getComputedStyle' of null
    at t (layout-default.js.pagespeed.jm.dRwxlhTSIt.js:10)
    at a.hide (layout-default.js.pagespeed.jm.dRwxlhTSIt.js:10)
    at a.invalidateState (layout-default.js.pagespeed.jm.dRwxlhTSIt.js:17)
    at a._focusInput (layout-default.js.pagespeed.jm.dRwxlhTSIt.js:17)
    at m.handleEvent (layout-default.js.pagespeed.jm.dRwxlhTSIt.js:10)

Usage without bower

Hi. I was intrested in your project from the first look (спасибо хабру). It's really BETTER DOM. So I've used it on a production in a small project build on node.js with a bower onboard. And it has worked as expected. Now I working on a Meteor.js and I have to stay away from bower (sadly). How can I get a "browsefied" build of betterDom? Is it possible?

Error when using application/xhtml+xml

I've got a page that uses application/xhtml+xml as its MIME type, and in Firefox (at least) when I attempt to include better-dom.js, the following appears in the console:

XML Parsing Error: mismatched tag. Expected: .
Location:
Line Number 1, Column 65:

SyntaxError: An invalid or illegal string was specified better-dom.js:179

RequireJS error

When better-dom.js gets loaded with RequireJS, it throws the following errors:

Uncaught TypeError: undefined is not a function – line 329
Uncaught TypeError: Object #<$Node> has no method 'ready' – line 176

What can we do about this?

Hooks support

It'd be nice to allow plugins to extend creation methods, so it's possible to write:

DOM.create("ul>li.list-item*3", "emmet");

instead of

DOM.create(DOM.emmet("ul>li.list-item*3"));

Also for event handlers hooks API can help to polyfill/fix some specific events for browsers (like IE8 fixes for the submit event etc.)

Update docs for version 3

  • microtemplating docs move to https://github.com/chemerisuk/better-emmet-plugin
  • display property support in visibility methods
  • changes in $Element#value implementation
  • $Element#data and getter/setter cleanup
  • no numeric properties in event handler functions
  • removal of requestFrame/cancelFrame, $Element#map, $Element#empty, DOM.format
  • default value argument in $Element#get

unsafe eval using better-dom in chrome extension

Came across this lib the other day and wanted to use it the next chance I got.. which happened to be a chrome extension.
When adding the lib, I get the following error:

Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self' chrome-extension-resource:".

The error pertains to this line:

return Function("cb", "that", "undefined", code);

Which is part of the makeLoopMethod function.

The quick work-around is to add unsafe-eval to the extension's CSP but it's not recommended.. thought I'd mention it in case there's an alternate way that better-dom could work... I've seen libs with similar issues attempt to detect if they're loaded in an extension or provide via a settings whether it would do something one way or an extension-compatible way. Chrome extensions are all loaded with chrome-extension:// in the url so that might be a way to detect.

Console Errors

First time using this. Haven't modified the files at all.

Uncaught HierarchyRequestError: A Node was inserted somewhere it doesn't belong.
    better-dom.js:2142
    (anonymous function) better-dom.js:2142
    (anonymous function) better-dom.js:2177
    (anonymous function) better-dom.js:2308

Uncaught TypeError: Cannot call method 'create' of undefined
    better-form-validation.js:38
    (anonymous function) better-form-validation.js:38
    (anonymous function) better-form-validation.js:244

Availability of Better-dom on a CDN

Note: There's no way for me (as a non-contributor) to mark this as more of an "enhancement request" in github, so I'm just creating an issue here. Feel free to close it if this is an inappropriate request.

I'm comfortable with using bower to generate the actual code from the github repo for my local projects, but one thing I've been looking into is just quickly creating experiments throughout the day using tools like codepen.io. Because of this, I debated ways to use just the code I need as an include (as you can see on codepen, you can very quickly use existing libraries or include a sample path yourself). Is there any plan to put better-dom on a CDN like cdnjs or something similar? The non-legacy version could be quickly added as a single file, I think, though I may be missing some understanding of the steps required during your build process.

ES6 template strings and smart quotes

Currently it's not possible to use ES6 template strings for emmet abbreviations, because both use back ticks. Need to find a workaround.

Example where template strings can be useful:

DOM.emmet("span>" + keys.map((key) => {
    return "span[data-l10n=`" + key + "`]>`" + this[key] + "`";
}).join("^"));

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.