1

my recursive function is breaking and I cannot figure out why. I am trying to write a parser for json to css with barebone javascript and can't seem to wrap my head around why my function doesn't work. Can anyone lend me a hand?

In css you would get elements that are children of that particular element you are addressing with something like this

#nav li{
  some style here
}

Trying to manipulate the same thing in javascript only it doesn't work. My code works fine for simple selectors so far.

var json = {
    "body" : {
        "background": "#303030",
        "color": "#FFFFFF",
        "font-family": "Arial, Helvetica, sans-serif"
    },
    "#nav": {
        "li": {
            "display": "inline-block",
            "list-styles": "none",
            "margin-right": "10px",
            "padding": "10px, 10px"
        }
    }
}
// How to use:
// json is an obj
// body is a selector
// background is a property belong to a selector
// #303030 is a value for the property


//apply css changes to document without writing any css
//by default the first values are
//               selector = background
//               parent   = document
//               object   = json
function styleApply(selector, parent, object){
    var element, onSelector, signal;

    //checks the first character of the selector and grabs element from page depending on what type of element it is
    switch(selector[0]){
        case ".":
            element= parent.getElementsByClassName(selector.substring(1));
            newParent = element[0]; //checks to see what is in the first index of the obtained element
            break;
        case "#":
            element= parent.getElementById(selector.substring(1));
            newParent = element;
            break;
        default:
            element= parent.getElementsByTagName(selector);
            newParent = element[0];
            break;
    }

    //only works if there is actually an element in the page corresponding with the selector
    if(newParent != null){ 
        //loops through all elements with the same selector or in the case of id just does one loop
        for(var i=0; i<element.length; i++){ 

            //loops through all properties in the selector
            for (var property in object[selector]){
                //grabs the associated value with the selector could be string or object
                var value= object[selector][property];

                //if it is a string it is a style, if it is an object, it is targeting the elements inside the current selector only
                if(typeof(value) === "string"){
                    element[i].style.setProperty(property, value);
                }else{

/*I am breaking my code right here*/

                    //reusing the same function, this time selector is equal to property for the case of nav, it is the element 'li'
                    //newParent is the element who is a parent of the current element (e.g 'nav' is parent of 'li')
                    //value is the new object that is replacing json, 
                    styleApply(property, newParent, value); //since value is an object it did not pass the string test and now the new object is value
/*Problem ends here */


                }
            }
        }
    } 
}

my code for looping over all the values in json
for (var selector in json){
    styleApply(selector, document, json);
}
4
  • getElementById returns a single element, not a collection. You need to wrap it in an array. Commented Apr 23, 2013 at 3:04
  • When you call your function recursively, you need to pass element[i] as the new parent, not newParent. Commented Apr 23, 2013 at 3:11
  • YAY Thanks a lot Barmar, I managed to shorten my code and got the inheritor working! and thanks xbonez for looking over my work. One extra change I made is in my value = object[selector] I needed to go back one param to replicate the structure of my json Commented Apr 23, 2013 at 4:14
  • Object iteration order is implementation dependent and browsers are not reliable with this, so this approach will not work consistently with css rules where order is important Commented Aug 6, 2013 at 5:25

2 Answers 2

1

I just happen to look into json to write a cleaner css than straight up concatenating strings and numbers together. Instead of applying the style directly to each element, I put them into a <style> element, which makes it easier to change throughout the lifetime of the page. It's also capable of targeting pseudo-elements such as :before, which I used.

Anyway, here's the function:

// convert json to css
function jsonToCss(obj) {
    var x, finalStr=''
    for (x in obj) {
        var y, decStr=''
        for (y in obj[x]) {
            decStr+= y + ':' + obj[x][y] + ';'
        }
        finalStr+= x + '{' + decStr + '}'
    }
    return finalStr
}

As the function only recurses through two levels (one for the selectors and one for the declarations) your json needs to be modified as follows:

var json = {
    "body" : {
        "background": "#303030",
        "color": "#FFFFFF",
        "font-family": "Arial, Helvetica, sans-serif"
    },
    "#nav li": {
        "display": "inline-block",
        "list-styles": "none",
        "margin-right": "10px",
        "padding": "10px, 10px"
    }
}

Feed that into the function and you get the following string:

body{background:#303030;color:#FFFFFF;font-family:Arial, Helvetica, sans-serif;}#nav li{display:inline-block;list-styles:none;margin-right:10px;padding:10px, 10px;}

Which you can then place into a style element. I haven't validated the strings produced by the function to see if it's well-formed but it works quite well on my end.

Sign up to request clarification or add additional context in comments.

Comments

0

It is a short little code I read about parsing json into css and I wanted to try it out

-- selectors are referring to css selectors that you write in json

-- parent starts out referring to the document as in the Document object model in javascript

-- object is the json object being passed in

please do correct me again if I misuse any jargon

function styleApply(selector, parent, object){
    var element, onSelector, signal;
    
    switch(selector[0]){
        case ".":
            element= parent.getElementsByClassName(selector.substring(1));
            break;
        case "#":
            element= [parent.getElementById(selector.substring(1))];
            break;
        default:
            element= parent.getElementsByTagName(selector);
            break;
    }
    
    if(element[0] != null){
        for(var i=0; i<element.length; i++){ 
            for (var property in object[selector]){
            
                var value= object[selector][property];

                if(typeof(value) === "string"){
                    element[i].style.setProperty(property, value);
                }else{
                    styleApply(property, element[i], object[selector]);
                }
            }
        }
    } 
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.