Convert XML to JSON with JavaScript

By  on  

If you follow me on Twitter, you know that I've been working on a super top secret mobile application using Appcelerator Titanium.  The experience has been great:  using JavaScript to create easy to write, easy to test, native mobile apps has been fun.  My mobile app connects to numerous social network APIs, some of which only provide an XML response.  My mini "framework" uses JSON to dynamically create widgets so I've needed a way to turn XML into JSON.  I found many solutions but none of them worked.  After tweaking an existing function, I've found a solution that works great.

The JavaScript

It's important to point out that Titanium's Titanium.XML.DOMDocument object implements DOM2-level structures.  Here's the magic XML to JSON code:

// Changes XML to JSON
function xmlToJson(xml) {
	
	// Create the return object
	var obj = {};

	if (xml.nodeType == 1) { // element
		// do attributes
		if (xml.attributes.length > 0) {
		obj["@attributes"] = {};
			for (var j = 0; j < xml.attributes.length; j++) {
				var attribute = xml.attributes.item(j);
				obj["@attributes"][attribute.nodeName] = attribute.nodeValue;
			}
		}
	} else if (xml.nodeType == 3) { // text
		obj = xml.nodeValue;
	}

	// do children
	if (xml.hasChildNodes()) {
		for(var i = 0; i < xml.childNodes.length; i++) {
			var item = xml.childNodes.item(i);
			var nodeName = item.nodeName;
			if (typeof(obj[nodeName]) == "undefined") {
				obj[nodeName] = xmlToJson(item);
			} else {
				if (typeof(obj[nodeName].push) == "undefined") {
					var old = obj[nodeName];
					obj[nodeName] = [];
					obj[nodeName].push(old);
				}
				obj[nodeName].push(xmlToJson(item));
			}
		}
	}
	return obj;
};

The major change I needed to implement was using attributes.item(j) instead of the attributes[j] that most of the scripts I found used.  With this function, XML that looks like:

<ALEXA VER="0.9" URL="davidwalsh.name/" HOME="0" AID="=">
	<SD TITLE="A" FLAGS="" HOST="davidwalsh.name">
		<TITLE TEXT="David Walsh Blog :: PHP, MySQL, CSS, Javascript, MooTools, and Everything Else"/>
		<LINKSIN NUM="1102"/>
		<SPEED TEXT="1421" PCT="51"/>
	</SD>
	<SD>
		<POPULARITY URL="davidwalsh.name/" TEXT="7131"/>
		<REACH RANK="5952"/>
		<RANK DELTA="-1648"/>
	</SD>
</ALEXA>

...becomes workable a JavaScript object with the following structure:

{
	"@attributes": {
		AID: "=",
		HOME:  0,
		URL: "davidwalsh.name/",
		VER: "0.9",
	},
	SD = [
		{
			"@attributes": {
				FLAGS: "",
				HOST: "davidwalsh.name",
				TITLE: A
			},
			LINKSIN: {
				"@attributes": {
					NUM: 1102
				}
			},
			SPEED: {
				"@attributes": {
					PCT: 51,
					TEXT: 1421
				}
			},
			TITLE: {
				"@attributes": {
					TEXT: "David Walsh Blog :: PHP, MySQL, CSS, Javascript, MooTools, and Everything Else",
				}
			},
		},
		{
			POPULARITY: {
				"@attributes": {
					TEXT: 7131,
					URL: "davidwalsh.name/"
				}
			},
			RANK: {
				"@attributes": {
					DELTA: "-1648"
				}
			},
			REACH: {
				"@attributes": {
					RANK = 5952
				}
			}
		}
	]
}

From here you can use the JavaScript object however you see fit. If you'd like the JavaScript in string JSON format, you can code:

// Assuming xmlDoc is the XML DOM Document
var jsonText = JSON.stringify(xmlToJson(xmlDoc));

This function has been extremely useful in allowing me to quickly disregard XML and use JSON instead.  The function works well when structuring attributes and arrays of nested child nodes.  Keep this handy;  at some point you may need to convert XML to JSON!

Recent Features

Incredible Demos

Discussion

  1. Nicely put. Good work!

  2. Pretty good topic, i am working in a ios project that needed localization and all kinda of stuffs and coredata was a pain in the ass to make it work, so i have made a xml file for each language and for my surprise, nsxmlparser is a sax parser and it really sucks, so i converted my xml file in a json with this site: http://shlang.com/xml2json/

    it worked and the json validated in the jsonlint.com

  3. Your code example has several incorrect references to $xmlToJson.

    I took a stab at cleaning up the xmlToJson function here.

  4. Rob james

    Is there any reason you’re not using the YQL function in titanium to do this? I’ve found it worked a treat, only I’m not sure how efficient it is.

    • My understanding is that I would need to then connect to Yahoo!’s servers. I’d prefer not to do that. They could change their service API, ban me, etc. Cutting YQL out cuts out a possible problem.

    • Rob James

      Yeah, probably the right decision based on those criteria. Though I had a tiny inkling that the YQL library was built in, it’s probably not, but is sure is fast!

      Cool, keep up the good work!

    • Lance Porter

      David’s foresight proved correct as YQL has now been retired. I find myself here because I needed a solution that wasn’t dependent on the life a 3rd party service! Thank you David!!

  5. Yes, JSON format need the quotes. Only keys of Objects in JavaScript can leave the quotating. It looks similar but dont mix it.

  6. Manually replacement? C’mon, regex replacement, live in the now! ;)

  7. You’re a rock star!

  8. Does item.nodeName include namespaces? I’m not sure what you would do in the case of namespace conflicts when converting to JSON, short of naming conventions.

  9. You may want to change the line that checks if the JS object is already an array from:

    if (typeof(obj[nodeName].length) == "undefined") {

    to:

    if (typeof(obj[nodeName].push) == "undefined") {

    As it currently is, the code will fail (on Chrome 10 at least) on XML that has whitespace nodes between the elements. The check for length will result in a false positive “yes, this #text item is an array”.

    With my modification the code will be able to handle XML document with whitespace nodes. It’ll still output lots of #text items in the arrays though and you’ll lose the order of the text nodes vs. sub-elements. YMMV.

    • Brilliant! Thank you so much for this fix.

    • You made this work for me – thanks!

    • I couldn’t figure out why David’s function was taking so long to parse my XML. I tried Frank van Puffelen’s fix and it is blazing fast now. It would take up to 20 seconds before and now it is in the milliseconds range. It was apparently failing silently in Titanium and every time it would fail, it would cause a costly operation to occur. Due to the number of iterations, it would cause the overall parsing be extremely slow.

      David– Thank you for providing this function. Would you please update your function to include this fix so additional visitors get the best version of this code?

    • Fixed! Thank you!

  10. spock

    Cool…I’m looking to go the other way with a twist.

    I need a simple way to take JSON output & convert it to CSV,
    but haven’t been able to locate any business user friendly utils for
    that.

    Thoughts?

  11. Is there anyway to grab CDATA values in your script?

  12. I have put together a little JSON sample that iterates over a JavaScript object and posts the property values to a cross domain server that is hosts by a DotNet.aspx page that then converts a C# object to a JSON string that is then posted back to the browser and converted back to a JavaScript object without having to use Window.Eval()

    The resultant JavaScript object is then finally past back to a call-back function that is ready to uses and the code does not need 3rd party libraries, works in net framework 2.0 and upwards and has been tested with IE6-IE9, Firefox plus it’s lightweight.

    Click my name for full details

  13. Thanks! Just started a titanium where a service only returns xml. Just the thing im looking for.

  14. John

    Hi!

    I’m trying to test your function in a webservice response but I can’t execute it. Always that I’ll try to compile and execute with Titanium SDK 1.7.5 it said me:

    [ERROR] Script Error = Result of expression ‘xml.hasChildNodes’ [undefined] is not a function. at XMLTools.js (line 22).

    I’m introducing your code in a file called XMLTools.js.

    Thanks so much!

    • Satish

      I’m getting the same error. Any idea on how to solve this? How do I pass an XML file to this function?

    • Richard

      I’m getting this error too. Did anyone ever find a fix for this?

  15. Awesome script! however, I’m getting the following Runtime Error:

    TypeError: Cannot find function hasChildNodes in object [Ti.NodeList]...

    Please advise.

    Thanks!

    • You’re not passing in a DOM element. If you’re using JQuery, the easiest thing is to call $(your_xml_string)[0] to convert it.

  16. great post David,
    I’m not sure if our tool could be of any help for people reading this post, but it converts any RSS (which is written in XML) or html to JSON on the fly, you might want to take a look at it , it’s http://www.feedsapi.com , so if you are looking for a quick utility to help you, check it out.

  17. Satish

    Hi,
    How do I pass an XML file to this function? I’m new to JavScript, and I keep getting ‘xml.hasChildNodes’ [undefined] is not a function.

    • Brian

      This happened to me aswell at first I realised I was using a string and not an xml doc

  18. Don’t know if anyone has mentioned it yet, but the test for array should be updated.

    if( Object.prototype.toString.call( obj[nodeName] ) !== '[object Array]' ) {
    //if (typeof(obj[nodeName].length) == "undefined") {
    

    If obj[nodeName] is a string it will have a length property.
    I think the prototype toString is pretty resilient.

  19. Rajesh

    This code is working fine on 1.8 SDK. but breaking on 2.1 SDK.

    Thans to FRANK VAN PUFFELEN and CLAUDE for providing fix.

  20. Rajesh

    One more fix for 2.1 sdk

    wrap xml.attributes.length if statement in another if

    if (xml.attributes) {
        if (xml.attributes.length > 0) {
            obj["@attributes"] = {};
            for (var j = 0; j < xml.attributes.length; j++) {
                var attribute = xml.attributes.item(j);
                obj["@attributes"][attribute.nodeName] = attribute.nodeValue;
            }
        }
    }
    
  21. This worked a bit better for me:
    https://gist.github.com/3861616

  22. This is a version that provides a JSON object without the attributes and places textNodes as values rather than an object with the textNode in it.

    https://gist.github.com/4153569

  23. https://gist.github.com/4153569

    This is a version that provides a JSON object without the attributes and places textNodes as values rather than an object with the textNode in it.

    • Leon

      Hi Ben!

      I’m looking for a version that converts the textNode to a value rather than an object as well, did you post this somewhere? Thanks!

  24. Mohan

    Could any one help me to get the reverse of this post. i had seen somewhere on this site to convert JSON to XML .unfortunately i could not find it now.

  25. Hi everyone. Really nice script. I have a question though. I need to print on the screen the values of the xml. I see that some of the json objects come in the following format when I look at it in firebug:
    link Object { #text=”Homes”} or
    header Object {#cdata-section=”My data is here”‘}

    How do I print the contents of both this object on the page? I’ve tried things like myObj.data, myObj.textValue but nothing seems to work. Any help would be appreciate it.
    Thanks
    TJ

    • based on the provided sample xml i.e.:

      console.log(jsonFromXml.ALEXA.SD.POPULARITY[‘@attributes’].TEXT);

    • myObj.textContent
      
  26. Hi, I’m very exited to see this function, good work!
    But in my application, I find too many whitespace text nodes under the #text property in my json object, they are useless and annoying, so I just want to remove them.
    Besides, I want my json object to be as simple as possible so as to make the code pretty, so I just extract all the attributes into the current node, e.g wnd.attributes.size => wnd.size, although it may destroy the original structure but seems simpler and make no mistake. I have verified the new json at JSONLint, it looks good.

    And here is my version, any ideas? :)

    // Changes XML to JSON
    function xmlToJson(xml) {
    	
    	// Create the return object
    	var obj = {};
    
    	if (xml.nodeType == 1) { // element
    		// do attributes
    		if (xml.attributes.length > 0) {
    		//obj["attributes"] = {};
    			for (var j = 0; j < xml.attributes.length; j++) {
    				var attribute = xml.attributes.item(j);
    				//obj["attributes"][attribute.nodeName] = attribute.nodeValue;
    				obj[attribute.nodeName] = attribute.nodeValue;
    			}
    		}
    	} else if (xml.nodeType == 3) { // text
    		obj = xml.nodeValue.trim(); // add trim here
    	}
    
    	// do children
    	if (xml.hasChildNodes()) {
    		for(var i = 0; i < xml.childNodes.length; i++) {
    			var item = xml.childNodes.item(i);
    			var nodeName = item.nodeName;
    			if (typeof(obj[nodeName]) == "undefined") {
    				var tmp = xmlToJson(item);
    				if(tmp != "") // if not empty string
    					obj[nodeName] = tmp;
    			} else {
    				if (typeof(obj[nodeName].push) == "undefined") {
    					var old = obj[nodeName];
    					obj[nodeName] = [];
    					obj[nodeName].push(old);
    				}
    				var tmp = xmlToJson(item);
    				if(tmp != "") // if not empty string
    					obj[nodeName].push(tmp);
    			}
    		}
    	}
    	return obj;
    };
    
    • @tjumyk
      Thanks for sharing your work.
      I only had to make some very small additions to get what I wanted.
      http://plnkr.co/edit/YLJSNv
      Make attributes property names start with an ‘@’ to avoid collisions.
      Makes a properties value equal null if its an empty object….checking if an object is empty is a pain ;).
      If the only property is a text node then dont return an object but a string…just like you wanted (and me) its really clean now.

  27. Very nice, I am researching the xml in javascript, but I am not sure that function you coded can be worked out in the IE6/ IE7… Thanks!

    • if you got any troubles with mediocre browsers…

      theres a rather new approach called “polyfills”…

      have a bit of the googs…

  28. i love to see this post, ^_^
    many thanks for your great function, (y)

  29. Hi
    I also thank you for this article Using your function presented in this page I created and used the javascript object posted on this page:
    http://coursesweb.net/javascript/convert-xml-json-javascript_s2
    It returns json object or string directly from file or string with xml.

  30. Trishul

    Awesome job, saved me a fortune.

  31. Isuru Ranawaka

    Great solution. Thanks Mr. Walsh!

  32. AnaC

    Hi!

    I’m having trouble converting XML to JSON, specfically RSS2.0 and ATOM with Javascript.

    I am actually looking for someone who can give me advice on specific questions. Can anyone help me?

    Thanks!!!

  33. Lakshmi

    This is really awesome! Great Work David.

  34. This tool will help you to test and validate XML to JSON content.

    http://codebeautify.org/xmltojson

  35. Jim Michaels

    all of them equally bad.
    debugger says
    “@http://j/calculators/PSU-calculators/power-supply-load-calculator2.html#app:476:1
    xml.hasChildNodes is not a function”

    or there some special way to use this? I am using output from jquery’s .get() using an xml source file on ff.

    • Content-Type when grabbing your xml to application/xml, default is application/json and since xml is not json…

      look below for howto do that.

  36. Jana

    Thanks for that, that was exactly what I needed :-)

  37. Nimesha

    Great work. but I have this error of my code

    TypeError: xml.hasChildNodes is not a function
    

    please give me a solution

    • Bhushan

      I am getting same error, were you able to get the solution over it?
      It would be great if you share :)

    • probably tryin’ to read in xml as json…, gotta give it the accurate Content-Type on grabbing… look below for solutions…

  38. Thanks for this code snippet :)

    A slightly modified version for generate more readable JSON (sets text value directly on key instead of creating an object with #text key): https://gist.github.com/chinchang/8106a82c56ad007e27b1

  39. Marcos Hernandez

    How can i get a attribute value once i’ve already have my json object, i mean all those attribute has a #text and i can’t reach the value. does anyone know?

  40. Mark

    I am trying to implement xmlToJson function to convert my xml generated on the server into a Javascript array. The problem I have is that when an xml element has ONE child that is a folder then that ONE child will be added as an object rather than an array with one member. Please help to modify the function to work to my specs.
    Thanks in advance.

    • Mark

      I’m in the same situation, did you ever find a solution?

    • Brook Monroe

      Many years later, I’ve run into the same issue, and I don’t think there’s a one-size-fits all way of solving the problem without using look-ahead, or back-tracking, neither of which are good for performance. Since XML has no explicit concept of arrays, it has no concept of Array[1], so how would one decide without using look-ahead or back-tracking?

  41. It’s all a big crap! No one is treating all nodetypes, …anyway is a nice start for a reliable xml2json function, ty David

  42. Dave

    Hello everyone! If you are involved in localization projects, I suggest to give https://poeditor.com/ a try. It is a collaborative translation management platform with a simple work interface, designed to automate the localization process. It has a free trial, so you test it and see if it helps.

  43. I found tool to do that http://codebeautify.org/jsontoxml

  44. Pranjal

    How to ignore root element while converting xml file to json?

  45. If you have a valid dtd file for the xml then you can easily transform “JSON to XML” and “XML to JSON” in JAVA using the eclipselink jar binary.

    Refer this: http://www.cubicrace.com/2015/06/How-to-convert-XML-to-JSON-format.html

  46. Scott

    This works great when I load a file with ajax, but if I use an input form field the media type is different and the conversion fails. Ideas?

  47. Thanks this helps. I am trying to develop a free open source online converter using your tutorial on this page http://tools.fromdev.com/ .

    Thanks for sharing. I guess the challenge is going to be getting the titanium xml implementation in client side JS library format. Can you please share the DOM lib location powering this code? I need something lightweight – if not available I can write my own or borrow from something.

  48. Jeff Hernandez

    I put together an API to this from most languages including Javascript. Let me know what you think!
    https://algorithmia.com/algorithms/Jeffro/xml2json#toc

  49. Kenneth

    Really like the article, thanks for straight forward way to handle this issue.

    I have a question about the line that looks like it checks for an array.

    if (typeof(obj[nodeName].push) == "undefined")
    

    Could someone explain why this line doesn’t produce an error, if it is undefined? – I tried to use this line on it own and it gives me an

    "Uncaught TypeError: Cannot read property 'push' of undefined at :1:17"
    

    Thanks for any feedback….

  50. Sudhakar

    can I pass directly xml file name for this function, there by it converts it to son object?

  51. I came across this and on a first hit all my problems went away before I could even encounter them.

    Good job sir and thank you for this.

  52. using xml2json.js library created bulk xml to json converter. http://xmltojson.tk,

  53. Kennedy

    How do I use this function?

    • you probably mean howd you call that:

              const myXml = 'http://some.com/xmlfeed';
              function XMLDoc(callback){
                  let xmlhttp = new XMLHttpRequest();
                  xmlhttp.onreadystatechange = () => {
                      //Success
                      if (xmlhttp.readyState === 4 && xmlhttp.status === 200) {
                          //output of xml
                          console.log(xmlhttp.responseXML);
                          callback(xmlhttp.responseXML);
                      }
                  };
                  xmlhttp.open('GET', myXml, true);
                  xmlhttp.send();
              }
              XMLDoc((e) => {
                  let jsonText = xmlToJson(e);
                  //output of json
                  console.log(jsonText);
              });
      
  54. just ecmascripted the thingy a bit, also put in some styling guides:

            function xmlToJson(xml) {
                // Create the return object
                var obj = {};
                if (xml.nodeType === 1) { // element
                    // do attributes
                    if (xml.attributes.length > 0) {
                        obj['@attributes'] = {};
                        Object.keys(xml.attributes).forEach((j) => {
                            let attribute = xml.attributes.item(j);
                            obj['@attributes'][attribute.nodeName] = attribute.nodeValue;
                        });
                    }
                } else if (xml.nodeType === 3) { // text
                    obj = xml.nodeValue;
                }
                // do children
                if (xml.hasChildNodes()) {
                    Object.keys(xml.childNodes).forEach((i) => {
                        let item = xml.childNodes.item(i);
                        let nodeName = item.nodeName;
                        if (!!typeof(obj[nodeName])) {
                            obj[nodeName] = xmlToJson(item);
                        } else {
                            if (!!typeof(obj[nodeName].push)) {
                                let old = obj[nodeName];
                                obj[nodeName] = [];
                                obj[nodeName].push(old);
                            }
                            obj[nodeName].push(xmlToJson(item));
                        }
                    });
                }
                return obj;
            }
    

    also, modern es6 stuff loves modern fetch function, if anyones got a solution for getting XML through “fetch api”, please, id love some input on that one…

  55. well, actually, inbetween i had a bit of a lookaround, and found this on the interwebs for replacing XMLHttpRequest

            //fetchin the xml es6 style...
            fetch(request).then((data) => {
                data.text().then((str) => {
                    let responseNu = new DOMParser().parseFromString(str, 'application/xml');
                    let jsonFromXml = xmlToJson(responseNu);
                    console.log(jsonFromXml);
                });
            });
    
  56. talking about getting the json… on certain databases one could nowadays just CONCAT or directly JSON_OBJECT or JSON_ARRAY on SELECT… might remove one level to the equation and make things less buggy and run smoother…

  57. Vlad Titov
    static xmlToJson(xml):string | any {
        // Create the return object
        let obj: any = {};
        if (xml.nodeType === 1) { // element
          // do attributes
          if (xml.attributes.length > 0) {
            obj['@attributes'] = {};
            for (let j = 0; j < xml.attributes.length; j++) {
              const attribute = xml.attributes.item(j);
              obj['@attributes'][attribute.nodeName] = attribute.nodeValue;
            }
          }
        } else if (xml.nodeType === 3) { // text
          const str = xml.nodeValue.trim();
          if (str.length) return str;
        } else if (xml.nodeType === 4) {
          return xml.data;
        }
        // do children
        if (xml.hasChildNodes()) {
          for (let i = 0, n = xml.childNodes.length; i < n; i++) {
            const item = xml.childNodes.item(i);
            const nodeName = item.nodeName;
    
            if (typeof(obj[nodeName]) === 'undefined') {
              const out = this.xmlToJson(item);
              if (Object.keys(out).length) obj[nodeName] = out;
            } else {
              const out2 = UTILS.xmlToJson(item);
              if (Object.keys(out2).length === 0) continue;
              if (typeof(obj[nodeName].push) === 'undefined') obj[nodeName] = [obj[nodeName], out2];
              else obj[nodeName].push(out2);
    
            }
          }
        }
        return obj;
      }
    
  58. Infernus

    Damn man, nice work, I’ve been stuck on sth like this for days!

  59. /** Changes XML to JSON*/
    function xmlToJson(_xml)
    {
    	var obj = {};
    	if(_xml.nodeType == 1)
    	{
    		/** ELEMENT_NODE */
    		if(_xml.attributes)
    		{
    			if(_xml.attributes.length > 0)
    			{
    				obj["@attributes"] = {};
    				for(var j = 0; j < _xml.attributes.length; j++)
    				{
    					var attribute = _xml.attributes.item(j);
    					obj["@attributes"][attribute.nodeName] = attribute.nodeValue;
    				}
    			}
    		}
    	}
    	else if(_xml.nodeType == 3)
    	{
    		/** TEXT_NODE #text */
    		return obj = (_xml.nodeValue == "\n") ? undefined : _xml.nodeValue;
    	}
    	else if(_xml.nodeType == 4)
    	{
    		/** DOCUMENT_POSITION_FOLLOWING #cdata-section */
    		return obj = _xml.data;
    	}
    	else if(_xml.nodeType == 9)
    	{
    		/** DOCUMENT_NODE */
    		obj["@attributes"] = {version: _xml.xmlVersion, encoding: _xml.xmlEncoding};
    	}
    	else
    	{
    		window.console.warn("Unprocessed xml node type exists " + _xml.nodeType);
    	}
    	/** do children */
    	if(_xml.hasChildNodes())
    	{
    		for(var i = 0; i < _xml.childNodes.length; i++)
    		{
    			var item = _xml.childNodes.item(i);
    			var nodeName = item.nodeName;
    			if(typeof(obj[nodeName]) == "undefined")
    			{
    				obj[nodeName] = window.parser.xmlToJson(item);
    			}
    			else
    			{
    				if(typeof(obj[nodeName].push) == "undefined")
    				{
    					var old = obj[nodeName];
    					obj[nodeName] = [];
    					obj[nodeName].push(old);
    				}
    				obj[nodeName].push(window.parser.xmlToJson(item));
    			}
    		}
    	}
    	return obj;
    };
    
    function xmlToJsonNoAttrs(_xml)
    {
    	var obj = {};
    	if(_xml.children.length > 0)
    	{
    		for(var i = 0; i < _xml.children.length; i++)
    		{
    			var item = _xml.children.item(i);
    			var nodeName = item.nodeName;
    			if(typeof(obj[nodeName]) == "undefined")
    			{
    				obj[nodeName] = window.parser.xmlToJsonNoAttrs(item);
    			}
    			else
    			{
    				if(typeof(obj[nodeName].push) == "undefined")
    				{
    					var old = obj[nodeName];
    					obj[nodeName] = [];
    					obj[nodeName].push(old);
    				}
    				obj[nodeName].push(window.parser.xmlToJsonNoAttrs(item));
    			}
    		}
    	}
    	else
    	{
    		obj = _xml.textContent;
    	}
    	return obj;
    };
    

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!