d3js not having the same behaviour within and outside a function

101 Views Asked by At

Very Strange !! If I make a d3 selection inside a function, I don"t get the same result as if it is outside ! The first element is skipped !! I am not even sure it could be a space-name environment issues :-@

If I do :

<!DOCTYPE html>
<html>
    <head>           
        <script type="text/javascript" src="http://mbostock.github.com/d3/d3.js"></script>
        <script>
        function start()
        {
            Selection           = d3.select("body");
            cours               = [ { titre : " 1"  }   , { titre : " 2"  }   , { titre : " 3"  }   ]   ;
            Updated_selection   = Selection.data(  cours );
            Updated_selection   .enter ()
                                .append("h1")
                                .text(function(d) { return d.titre ; }    );

        }
        </script>
    </head>
    <body onload=start()></body>
</html>

I got the only two last values rather than 3 !! :

2
3

But if I put the same code outside the function, without calling it onload :

<!DOCTYPE html>
<html>
    <head>           
        <script type="text/javascript" src="http://mbostock.github.com/d3/d3.js"></script>
        <script>

            Selection           = d3.select("body");
            cours               = [ { titre : " 1"  }   , { titre : " 2"  }   , { titre : " 3"  }   ]   ;
            Updated_selection   = Selection.data(  cours );
            Updated_selection   .enter ()
                                .append("h1")
                                .text(function(d) { return d.titre ; }    );


        </script>
    </head>
    <body></body>
</html>

Then I got my three values back !

1 2 3

I am missing something, but what ??

1

There are 1 best solutions below

0
On BEST ANSWER

The first time you run the function, the body element already exists. In your code, you're doing:

Selection           = d3.select("body");
cours               = [ { titre : " 1"  }   , { titre : " 2"  }   , { titre : " 3"  }   ]   ;

Updated_selection   = Selection.data(  cours );
Updated_selection   .enter ()
                    .append("h1")

When you bind your data to DOM elements with the .data(...) call you're binding three data elements to an array of DOM elements. In this case, you've selected all elements that are body tags and there is 1 body tag in your DOM already. Since you've got more data values in your array than there are items in the D3 selection, D3 will create "placeholder" elements for the additional items in your data array.

Then you are calling .enter() which will contain an array of the placeholder elements. That is, it contains the NEW elements which don't yet exist in the DOM. Since it only includes the NEW elements, it will only get called for the second and third data value in your array.

In the second case, your function is running before the body element has been created, so when you perform the select, an array with no elements is returned. You're then binding that to an array of data with 3 values. When the .enter() is called, you'll be iterating over 3 elements, rather than the 2 as before. This results in the difference that you've observed.

I suspect (based on your code) that what you really wanted to do was this:

        function start()
        {
            Selection           = d3.select("body h1");
            cours               = [ { titre : " 1"  }   , { titre : " 2"  }   , { titre : " 3"  }   ]   ;
            Updated_selection   = Selection.data(  cours );
            Updated_selection   .enter ()
                                .append("h1")
                                .text(function(d) { return d.titre ; }    );

        }
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<body onload=start()></body>

Note, the difference here is in the d3.select("body h1") line. I'm selecting all h1 elements, instead of the body element. This will result in an empty selection. When the data array is bound to the selection and .enter() is called, three new placeholders will be created.

A general rule of thumb is that your d3.select(...) call should reference the same elements as you later .append(...) in your .enter() call. This is actually a pattern with D3, and it's worthwhile you having a read of http://bost.ocks.org/mike/join/ until you understand it, ie. read it a couple of times.