A Pen by Gabriele Corti on CodePen.
Last active
May 14, 2020 02:01
-
-
Save w3tweaks/cc2ce8c2c1d7400053f617af4929f914 to your computer and use it in GitHub Desktop.
Demo 3
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!-- markup structure | |
| .app, wrapping container | |
| header, introducing the visualization | |
| .input, showing the array behind the visualization | |
| .visualization, showing one circle for each data point | |
| .controls, allowing to add or remove data points in the array | |
| .selection, highlighting the selections after each operation | |
| --> | |
| <div class="app"> | |
| <header> | |
| <h1>D3 Data Binding</h1> | |
| </header> | |
| <div class="input"> | |
| <p> | |
| <strong>Input array:</strong> | |
| <!-- include here the array describing the data points --> | |
| <span></span> | |
| </p> | |
| </div> | |
| <!-- include here one sphere for each data point --> | |
| <main class="visualization"></main> | |
| <div class="controls"> | |
| <button class="controls--add">Add data point</button> | |
| <button class="controls--remove">Remove data point</button> | |
| </div> | |
| <!-- include in each paragraph the data points described in the enter/update/exit selection --> | |
| <div class="selection"> | |
| <div class="selection--enter"> | |
| <h3>Enter Selection</h3> | |
| <p></p> | |
| </div> | |
| <div class="selection--update"> | |
| <h3>Update Selection</h3> | |
| <p></p> | |
| </div> | |
| <div class="selection--exit"> | |
| <h3>Exit Selection</h3> | |
| <p></p> | |
| </div> | |
| </div> | |
| </div> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // utility functions to generate a random data point | |
| const randomInt = (max = 10) => Math.floor(Math.random() * max); | |
| const randomLetter = () => String.fromCharCode(Math.floor(Math.random() * ('Z'.charCodeAt() - 'A'.charCodeAt()) + 'A'.charCodeAt())); | |
| // specify a unique identifier to differentiate the data points | |
| const randomDataPoint = () => ({ | |
| value: `${randomInt()}${randomLetter()}`, | |
| id: Math.random(), | |
| }); | |
| const dataPoints = 10; | |
| // array describing the data | |
| let data = Array(dataPoints).fill('').map(() => randomDataPoint()); | |
| // function visualizing the data points | |
| function visualizeData(inputData) { | |
| const visualization = d3 | |
| .select('.visualization'); | |
| // update selection, existing nodes | |
| const updateSelection = visualization | |
| .selectAll('div') | |
| .data(inputData, d => d.id); // object constancy, bind each element according to the id of the connected data point | |
| // enter selection, to be added | |
| const enterSelection = updateSelection | |
| .enter(); | |
| // exit selection, to be removed | |
| const exitSelection = updateSelection | |
| .exit(); | |
| // style the update selection with the matching class | |
| updateSelection | |
| .attr('class', 'selection--update'); | |
| // add a div for each new data point | |
| enterSelection | |
| .append('div') | |
| .attr('class', 'selection--enter') | |
| .text(d => d.value) | |
| // transition the div from 0 to 1 | |
| .style('transform', 'scale(0)') | |
| .transition() | |
| .delay((d, i) => 200 + 50 * i) | |
| .duration(500) | |
| .style('transform', 'scale(1)'); | |
| // remove the div for each no-longer-existing data point | |
| exitSelection | |
| .attr('class', 'selection--exit') | |
| // remove after the transition | |
| // ! if two elements are removed in succession and before the transition is complete the exit selection will consider both | |
| .transition() | |
| .delay(200) | |
| .duration(1000) | |
| .style('transform', 'scale(0)') | |
| .remove(); | |
| // complete the visualization updating the UI of the application | |
| // include the data points in the prescribed span | |
| d3 | |
| .select('.input p span') | |
| .html(inputData.map(({ value }) => value)); | |
| // add the selections to their respective container | |
| d3 | |
| .select('.selection .selection--enter p') | |
| .text(enterSelection._groups[0].map(group => group.__data__.value)); | |
| d3 | |
| .select('.selection .selection--update p') | |
| .text(updateSelection._groups[0].map(group => group.__data__.value)); | |
| d3 | |
| .select('.selection .selection--exit p') | |
| .text(exitSelection._groups[0].map(group => group.__data__.value)); | |
| } | |
| // immediately call the function to visualize the existing data | |
| visualizeData(data); | |
| // call the function following a click event on the .controls buttons, afte adding / removing a data point from the array | |
| // ! add / remove a data point at a random index of the array | |
| function addDataPoint() { | |
| const randomIndex = randomInt(data.length); | |
| data = [...data.slice(0, randomIndex), randomDataPoint(), ...data.slice(randomIndex)]; | |
| visualizeData(data); | |
| } | |
| // function removing a data point | |
| function removeDataPoint() { | |
| const randomIndex = randomInt(data.length); | |
| data = [...data.slice(0, randomIndex), ...data.slice(randomIndex + 1)]; | |
| visualizeData(data); | |
| } | |
| // bind the function | |
| d3 | |
| .select('.controls--add') | |
| .on('click', addDataPoint); | |
| d3 | |
| .select('.controls--remove') | |
| .on('click', removeDataPoint); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <script src="https://unpkg.com/d3@5.9.7/dist/d3.min.js"></script> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| @import url("https://fonts.googleapis.com/css?family=Lato:400,900&display=swap"); | |
| * { | |
| box-sizing: border-box; | |
| padding: 0; | |
| margin: 0; | |
| } | |
| body { | |
| min-height: 100vh; | |
| background: repeating-linear-gradient( | |
| -45deg, | |
| hsl(0, 0%, 94%) 0px, | |
| hsl(0, 0%, 94%) 5px, | |
| transparent 0px, | |
| transparent 40px | |
| ), | |
| repeating-linear-gradient( | |
| -45deg, | |
| hsl(0, 0%, 94%) 0px, | |
| hsl(0, 0%, 94%) 10px, | |
| transparent 0px, | |
| transparent 100px | |
| ), | |
| hsl(0, 0%, 97%); | |
| font-family: "Lato", sans-serif; | |
| } | |
| .app { | |
| background: #fff; | |
| padding: 1rem 2rem; | |
| max-width: 900px; | |
| margin: 2rem auto; | |
| min-height: 80vh; | |
| box-shadow: 0 1px 10px hsla(0, 0%, 0%, 0.1); | |
| /* display the containers in a column */ | |
| display: flex; | |
| flex-direction: column; | |
| } | |
| /* separate the direct children vertically */ | |
| .app > * { | |
| margin: 1rem 0; | |
| } | |
| /* stretch the visualization to cover the available space, if any */ | |
| .visualization { | |
| flex-grow: 1; | |
| /* display the circle in a wrapping row */ | |
| display: flex; | |
| flex-wrap: wrap; | |
| } | |
| /* style the div included for the data points as circles */ | |
| .visualization div { | |
| width: 48px; | |
| height: 48px; | |
| margin: 0.5rem; | |
| line-height: 48px; | |
| border-radius: 50%; | |
| text-align: center; | |
| font-weight: 900; | |
| } | |
| /* center the controls */ | |
| .controls { | |
| display: flex; | |
| justify-content: center; | |
| } | |
| .controls button { | |
| margin: 0 1rem; | |
| padding: 0.5rem 1rem; | |
| background: hsl(0, 0%, 20%); | |
| color: hsl(0, 0%, 94%); | |
| border: none; | |
| border-radius: 5px; | |
| box-shadow: 0 1px 5px hsla(0, 0%, 0%, 0.1); | |
| font-family: inherit; | |
| font-size: 1rem; | |
| text-transform: uppercase; | |
| letter-spacing: 0.1rem; | |
| } | |
| .controls button:active { | |
| transform: translateY(2px); | |
| } | |
| /* display the selections in a wrapping row */ | |
| .selection { | |
| display: flex; | |
| flex-wrap: wrap; | |
| justify-content: space-around; | |
| } | |
| .selection div { | |
| margin: 1rem; | |
| border-radius: 20px; | |
| padding: 1rem; | |
| } | |
| /* style the classes describing the different selection with a different background and border-style */ | |
| .selection--update { | |
| background: hsl(240, 75%, 90%); | |
| border: 2px solid currentColor; | |
| } | |
| .selection--enter { | |
| background: hsl(120, 75%, 90%); | |
| border: 2px dashed currentColor; | |
| } | |
| .selection--exit { | |
| background: hsl(0, 75%, 90%); | |
| border: 2px dotted currentColor; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment