banner



How To Make An Animated Bar Graph Css

  • 21 min read
  • Coding, CSS, jQuery, Data Visualization, Blitheness

Quick summary ↬

People in boardrooms across the world beloved a skillful graph. They become basics for PowerPoint, bullet points and phrases like "run information technology upwards the flagpole," "blue-heaven thinking" and "low-hanging fruit," and everything is always "moving forward." Backwards is not an option for people who facilitate prototype shifts in the zeitgeist. Graphs of fiscal projections, quarterly sales figures and market place saturation are a center-manager'south dream.

html graph

How can we every bit Web designers get in on all of this hot graph action? There are really quite a few ways to display graphs on the Web. We could only create an image and blast information technology to a Web page. Merely that'south not very accessible or interesting. We could use Flash, which is quite good for displaying graphs — but once again, not very accessible. Besides, designers, developers and deities are falling out of dear with Flash.

Technologies such as HTML5 tin can do many of the same things without the demand for a plug-in. The new HTML5 <sail> chemical element could even exist adjusted to the job. Plenty of charting tools are online that nosotros might use. But what if nosotros wanted something a little more tailored?

Further Reading on SmashingMag:

  • Chartist.js, An Open-Source Library For Responsive Charts
  • Functional Blitheness In UX Design
  • Upgrading CSS Animation With Motion Curves
  • Creating Graphs With Adobe Illustrator

More afterwards jump! Proceed reading below ↓

There are pros and cons to the broad range of resources available to us, merely this tutorial volition not explore them all. Instead, nosotros'll create our graph using a progressively enhanced sprinkling of CSS3 and jQuery. Because we can.

What Are We Making?

We're making this. And more! Here are some possibilities on how you can extend the techniques explored in this tutorial:

  • A progress bar that indicates how long until the end of all humanity in the event of a zombie plague;
  • A graph indicating the decline in safe outdoor activities during a zombie plague;
  • A frighteningly similar graph indicating the refuse in manners during a zombie plague;
  • The increase of people who were unaware of the zombie plague because they were sharing with all of their at present-deceased friends on Facebook what they did on FarmVille.

Or you could create a graph or quota bar that just illustrates something useful and less full of dread and zombies. And so, let'southward get on with information technology.

What You'll Demand

  • A text or HTML editor. Take your option; many are out there.
  • jQuery. Practice safe scripting and become the latest one. Keep the jQuery website open so that y'all can look up the documentation every bit you go.
  • Possibly an image editor, such as Paint, to mock upwards what your graph might look similar.
  • A modern and decent Web browser to preview changes.

That should practice it. Please note that this tutorial is not designed as an introduction to either HTML, CSS, jQuery or zombies. Some intermediate knowledge of these three technologies and the undead is causeless.

The Mark-Up

You can create the underlying HTML for a graph in a number of ways. In this tutorial, we'll start with a table, because it will make the most sense visually if JavaScript or CSS is non applied. That's a big checkmark in the column for accessibility.

Quick! You've merely been given some alarming figures. The population of tanned zombies is projected to spiral out of control in the side by side few years. The carbon tigers and blue monkeys are under immediate threat. And so the tanned zombies will probably come for the states. Merely you're but a designer. What could you possibly exercise to assistance?

I know! You lot could make a Web page that illustrates our imminent demise with nice, calming, smoothly animated graphics!

To brainstorm, let'southward put this data into a table, with columns for each yr, and rows for the different species.

            <!doctype html> <html lang="en">    <head>       <meta charset="utf-8">       <meta name="viewport" content="width=1024">       <title>Example 01: No CSS</title>    </caput>     <body>       <div id="wrapper">          <div class="chart">             <h3>Population of endangered species from 2012 to 2016</h3>             <table id="data-tabular array" edge="1" cellpadding="x" cellspacing="0"             summary="The effects of the zombie outbreak on the populations             of endangered species from 2012 to 2016">                <explanation>Population in thousands</caption>                <thead>                   <tr>                      <td>&nbsp;</td>                      <th telescopic="col">2012</th>                      <th scope="col">2013</th>                      <th telescopic="col">2014</thursday>                      <th telescopic="col">2015</th>                      <th scope="col">2016</th>                   </tr>                </thead>                <tbody>                   <tr>                      <th telescopic="row">Carbon Tiger</th>                      <td>4080</td>                      <td>6080</td>                      <td>6240</td>                      <td>3520</td>                      <td>2240</td>                   </tr>                   <tr>                      <th scope="row">Bluish Monkey</th>                      <td>5680</td>                      <td>6880</td>                      <td>6240</td>                      <td>5120</td>                      <td>2640</td>                   </tr>                   <tr>                      <th scope="row">Tanned Zombie</thursday>                      <td>1040</td>                      <td>1760</td>                      <td>2880</td>                      <td>4720</td>                      <td>7520</td>                   </tr>                </tbody>             </table>          </div>       </div>    </body> </html>          

View the example beneath to run into how it looks naked, with no CSS or JavaScript practical. The accessibility of this table will enable people using screen readers to understand the data and the underlying message, which is "Run for your life! The zombies are coming!"

screenshot

The easy part is now out of the way. At present, let's tap into the power of CSS and JavasScript (via jQuery) to actually illustrate what the numbers are telling united states of america. Technically, our aim is to create a graph that works in all modernistic browsers, from IE 8 on.

Did I say all modern browsers? IE eight is lucky: it gets to hang out with the cool kids. Browsers that support CSS3 will get a few actress sprinkles.

"By Your Powers Combined…"

If you wish to summon Captain Planet, you may take to await elsewhere. If yous want to learn how to combine CSS and jQuery to create a graph that illustrates our impending doom at the easily of a growing army of zombies who prefer bronzer over brains, then read on.

The commencement thing to practice is style our table with some basic CSS. This is a overnice safety cyberspace for people who haven't enabled JavaScript in their browser.

screenshot

Getting Started In jQuery

Nosotros'll employ jQuery to create our graph on the wing, dissever from the original data tabular array. To do this, we need to get the information out of the tabular array and store it in a more usable format. So, nosotros can add to our certificate new elements that utilise this information in social club to construct our graph.

Let's get started past creating our chief createGraph() function. I've abbreviated some of the inner workings of this function and then that you become a clearer picture of the structure. Don't forget: you can always refer to the source code that comes with this tutorial.

Here'south our basic structure:

            // Wait for the DOM to load everything, just to be safe $(document).ready(function() {     // Create our graph from the information table and specify a container to put the graph in    createGraph('#data-table', '.chart');     // Hither exist graphs    function createGraph(data, container) {       // Declare some common variables and container elements       …        // Create the table data object       var tableData = {          …       }        // Useful variables to access table data       …        // Construct the graph       …        // Set the individual heights of bars       function displayGraph(confined) {          …       }        // Reset the graph'southward settings and prepare for brandish       function resetGraph() {          …          displayGraph(bars);       }        // Helper functions       …        // Finally, display the graph via reset role       resetGraph();    } });          

We laissez passer two parameters to this function:

  1. The data, in the form of a tabular array chemical element;
  2. A container element, where we'd like to place our graph in the document.

Next up, we'll declare some variables to manage our information and container elements, plus some timer variables for animation. Hither's the code:

            // Declare some mutual variables and container elements var bars = []; var figureContainer = $('<div id="figure"></div>'); var graphContainer = $('<div class="graph"></div>'); var barContainer = $('<div class="bars"></div>'); var data = $(data); var container = $(container); var chartData; var chartYMax; var columnGroups;  // Timer variables var barTimer; var graphTimer;          

Nothing besides exciting here, but these will be very useful later.

Getting The Data

Besides simply displaying the data, a proficient bar chart should take a dainty large championship, clearly labelled axes and a colour-coded legend. We'll need to strip the data out of the tabular array and format it in a way that is more meaningful in a graph. To do that, we'll create a JavaScript object that stores our information in handy footling functions. Allow'southward give nascence to our tableData{} object:

            // Create table information object var tableData = {    // Become numerical data from table cells    chartData: function() {       …    },    // Get heading data from table caption    chartHeading: function() {       …    },    // Get legend data from table body    chartLegend: function() {       …    },    // Become highest value for y-axis scale    chartYMax: function() {       …    },    // Become y-axis data from table cells    yLegend: function() {       …    },    // Get 10-axis data from table header    xLegend: function() {       …    },    // Sort data into groups based on number of columns    columnGroups: function() {       …    } }          

We have several functions here, and they are explained in the code's comments. Nigh of them are quite similar, so nosotros don't need to get through each one. Instead, let's pick apart ane of them, columnGroups:

            // Sort information into groups based on number of columns columnGroups: office() {    var columnGroups = [];    // Get number of columns from showtime row of table body    var columns = data.detect('tbody tr:eq(0) td').length;    for (var i = 0; i < columns; i++) {       columnGroups[i] = [];       information.detect('tbody tr').each(function() {          columnGroups[i].push($(this).find('td').eq(i).text());       });    }    return columnGroups; }          

Here's how it breaks down:

  • Create the columnGroups[] array to store the data;
  • Get the number of columns by counting the tabular array cells (td) in the outset row;
  • For each column, notice the number of rows in the tabular array body (tbody), and create some other array to store the table cell data;
  • And then loop through each row and grab the data from each tabular array prison cell (via the jQuery text() function), and and so add it to the table cell data array.

Once our object is full of juicy data, nosotros tin can start creating the elements that make upwardly our graph.

Using The Data

Using the jQuery $.each function, we can at present loop through our data at any indicate and create the elements that make up our graph. One of the trickier bits involves inserting the confined that represent each species inside the yearly columns.

Here's the lawmaking:

            // Loop through column groups, calculation bars as nosotros go $.each(columnGroups, role(i) {    // Create bar group container    var barGroup = $('<div grade="bar-group"></div>');    // Add confined within each column    for (var j = 0, k = columnGroups[i].length; j < k; j++) {       // Create bar object to store properties (label, meridian, lawmaking, etc.) and add it to array       // Set the elevation later in displayGraph() to allow for left-to-right sequential brandish       var barObj = {};       barObj.label = this[j];       barObj.height = Math.floor(barObj.label / chartYMax * 100) + '%';       barObj.bar = $('<div form="bar fig' + j + '"><span>' + barObj.label + '</span></div>')          .appendTo(barGroup);       bars.push(barObj);    }    // Add bar groups to graph    barGroup.appendTo(barContainer); });          

Excluding the headings, our table has 5 columns with three rows. For our graph, this means that for each column we create, three bars will appear in that cavalcade. The post-obit image shows how our graph will be constructed:

screenshot

Breaking it down:

  • For each column, create a container div;
  • Loop inside each cavalcade to become the row and cell information;
  • Create a bar object (barObj{}) to store the properties for each bar, such as its label, pinnacle and mark-up;
  • Add the mark-up property to the column, applying a CSS class of '.fig' + j to color lawmaking each bar in the column, wrapping the label in a span;
  • Add the object to our bars[] array so that we tin access the information later;
  • Piece it all together by calculation the columns to a container element.

Bonus points if you noticed that nosotros didn't set the height of the bars. This is and so that we have more control afterward over how the bars are displayed.

Now that nosotros have our bars, let's work on labelling our graph. Because the code to display the labels is quite similar, talking y'all through all of it won't be necessary. Hither's how we display the y-axis:

            // Add y-axis to graph var yLegend   = tableData.yLegend(); var yAxisList   = $('<ul form="y-centrality"></ul>'); $.each(yLegend, function(i) {    var listItem = $('<li><span>' + this + '</span></li>')       .appendTo(yAxisList); }); yAxisList.appendTo(graphContainer);          

This breaks down as follows:

  • Get the relevant tabular array information for our labels,
  • Create an unordered list (ul) to incorporate our list items;
  • Loop through the characterization data, and create a list item (li) for each characterization, wrapping each label in a span;
  • Attach the list item to our list;
  • Finally, adhere the list to a container element.

By repeating this technique, we can add the legend, ten-axis labels and headings for our graph.

Before we can display our graph, nosotros need to make certain that everything we've done is added to our container element.

            // Add bars to graph barContainer.appendTo(graphContainer);  // Add graph to graph container graphContainer.appendTo(figureContainer);  // Add together graph container to main container figureContainer.appendTo(container);          

Displaying The Information

All that'south left to practice in jQuery is set the peak of each bar. This is where our earlier work, storing the summit holding in a bar object, will come in handy.

We're going to breathing our graph sequentially, i by one, uno por uno.

I possible solution is to apply a callback function to animate the next bar when the concluding animation is complete. Yet, the graph would take too long to animate. Instead, our graph will utilise a timer office to display each bar after a certain amount of time, regardless of how long each bar takes to grow. Rad!

Here's the displayGraph() function:

            // Set the individual peak of bars function displayGraph(bars, i) {    // Changed the style we loop because of issues with $.each not resetting properly    if (i < confined.length) {       // Animate the peak using the jQuery breathing() function       $(bars[i].bar).animate({          height: confined[i].height       }, 800);       // Wait the specified time, and so run the displayGraph() office again for the next bar       barTimer = setTimeout(function() {          i++;          displayGraph(bars, i);       }, 100);    } }          

What'south that y'all say? "Why aren't you using the $.each role like you accept everywhere else?" Skilful question. Commencement, permit'south talk over what the displayGraph() part does, and then why it is the way it is.

The displayGraph() function accepts two parameters:

  1. The bars to loop through,
  2. An index (i) from which to start iterating (starting at 0).

Let'due south break down the rest:

  • If the value of i is less than the number of bars, and then keep going;
  • Get the current bar from the array using the value of i;
  • Animate the height holding (calculated every bit a percentage and stored in confined[i].tiptop);
  • Look 100 milliseconds;
  • Increment i past 1 and echo the process for the adjacent bar.

"So, why wouldn't yous just employ the $.each function with a delay() earlier the blitheness?"

Y'all could, and it would piece of work just fine… the starting time fourth dimension. But if you tried to reset the animation via the "Reset graph" push button, then the timing events wouldn't articulate properly and the bars would animate out of sequence.

I would similar to exist proven wrong, and if there is a amend fashion to practise this, feel free to sound off in the comments section.

Moving on, here's resetGraph():

            // Reset graph settings and prepare for display function resetGraph() {    // Finish all animations and set the bar's height to 0    $.each(confined, function(i) {       $(bars[i].bar).cease().css('tiptop', 0);    });     // Clear timers    clearTimeout(barTimer);    clearTimeout(graphTimer);     // Restart timer    graphTimer = setTimeout(function() {       displayGraph(bars, 0);    }, 200); }          

Let's interruption resetGraph() down:

  • Stop all animations, and prepare the height of each bar dorsum to 0;
  • Clear out the timers so that there are no devious animations;
  • Expect 200 milliseconds;
  • Call displayGraph() to animate the start bar (at index 0).

Finally, phone call resetGraph() at the lesser of createGraph(), and watch the magic happen equally we bask in the celebrity of our hard piece of work.

Non then fast, sunshine! Before we go any further, we need to put some clothes on.

The CSS

The offset matter we demand to practise is hide the original data table. We could practise this in a number of means, merely because our CSS volition load well before the JavaScript, let's do this in the easiest way possible:

            #data-table {    display: none; }          

Done. Let'south create a overnice container expanse to put our graph in. Because a few unordered lists are existence used to make our graph, we'll also reset the styles for those. Giving the #figure and .graph elements a position: relative is important because information technology volition anchor the place elements exactly where we want in those containers.

            /* Containers */  #wrapper {    height: 420px;    left: 50%;    margin: -210px 0 0 -270px;    position: accented;    top: 50%;    width: 540px; }  #figure {    height: 380px;    position: relative; }  #figure ul {    list-way: none;    margin: 0;    padding: 0; }  .graph {    top: 283px;    position: relative; }          

Now for the legend. We position the fable right down to the bottom of its container (#figure) and line up the items horizontally:

            /* Fable */  .legend {    background: #f0f0f0;    edge-radius: 4px;    lesser: 0;    position: absolute;    text-marshal: left;    width: 100%; }  .legend li {    display: block;    float: left;    summit: 20px;    margin: 0;    padding: 10px 30px;    width: 120px; }  .legend span.icon {    background-position: 50% 0;    border-radius: 2px;    display: block;    float: left;    elevation: 16px;    margin: 2px 10px 0 0;    width: 16px; }          

The x-axis is very similar to the fable. We line up the elements horizontally and anchor them to the bottom of its container (.graph):

            /* x-axis */  .ten-axis {    lesser: 0;    colour: #555;    position: accented;    text-align: middle;    width: 100%; }  .x-axis li {    float: left;    margin: 0 15px;    padding: 5px 0;    width: 76px; }          

The y-axis is a petty more than involved and requires a couple of tricks. We give it a position: accented to suspension it out of the normal menstruum of content, merely anchored to its container. We stretch out each li to the total width of the graph and add together a border across the top. This volition give u.s.a. some nice horizontal lines in the background.

Using the power of negative margins, we tin can offset the numerical labels inside the span and so that they shift upward and to the left. Lovely!

            /* y-axis */  .y-centrality {    color: #555;    position: absolute;    text-align: correct;    width: 100%; }  .y-axis li {    border-top: 1px solid #ccc;    display: block;    tiptop: 62px;    width: 100%; }  .y-axis li span {    brandish: block;    margin: -10px 0 0 -60px;    padding: 0 10px;    width: 40px; }          

At present for the meat in our endangered species sandwich: the bars themselves. Permit'due south start with the container element for the bars and the columns:

            /* Graph bars */  .bars {    height: 253px;    position: absolute;    width: 100%;    z-index: 10; }  .bar-group {    float: left;    acme: 100%;    margin: 0 15px;    position: relative;    width: 76px; }          

Nothing as well complicated here. We're simply setting some dimensions for the container, and setting a z-alphabetize to make sure it appears in front of the y-axis markings.

Now for each individual .bar:

            .bar {    border-radius: 3px 3px 0 0;    bottom: 0;    cursor: pointer;    height: 0;    position: absolute;    text-align: heart;    width: 24px; }  .bar.fig0 {    left: 0; }  .bar.fig1 {    left: 26px; }  .bar.fig2 {    left: 52px; }                      

The main styles to notation here are:

  • position: absolute and lesser: 0, which means that the bars volition be fastened to the bottom of our graph and grow up;
  • the bar for each species (.fig0, .fig1 and .fig2), which will be positioned within .bar-group.

Now, why don't we minimize the number of abrupt edges on any given page by using the border-radius property to round the edges of the top-left and superlative-right corners of each bar? OK, so border-radius isn't really necessary, but it adds a nice touch for browsers that back up it. Thankfully, the latest versions of the most popular browsers practice support it.

Considering we've placed the values from each table prison cell in each bar, we tin can add a peachy little pop-up that appears when you hover over a bar:

            .bar bridge {    #fefefe url(../images/info-bg.gif) 0 100% repeat-ten;    border-radius: 3px;    left: -8px;    brandish: none;    margin: 0;    position: relative;    text-shadow: rgba(255, 255, 255, 0.eight) 0 1px 0;    width: 40px;    z-alphabetize: 20;     -webkit-box-shadow: rgba(0, 0, 0, 0.6) 0 1px 4px;    box-shadow: rgba(0, 0, 0, 0.6) 0 1px 4px; }  .bar:hover span {    display: block;    margin-top: -25px; }          

Starting time, the pop-upwards is hidden from view via display: none. Then, when a .bar element is hovered over, we've gear up display: block to bring it into view, and fix a negative margin-summit to make it announced higher up each bar.

The text-shadow, rgba and box-shadow backdrop are currently supported by most modern browsers as is. Of these modern browsers, only Safari requires a vendor prefix (-webkit-) to make box-shadow work. Note that these backdrop are only enhancements to our graph and aren't required to empathise it. Our baseline of Cyberspace Explorer 8 merely ignores them.

Our final step in bringing everything together is to color code each bar:

            .fig0 {    background: #747474 url(../images/bar-01-bg.gif) 0 0 repeat-y; }  .fig1 {    groundwork: #65c2e8 url(../images/bar-02-bg.gif) 0 0 repeat-y; }  .fig2 {    background: #eea151 url(../images/bar-03-bg.gif) 0 0 repeat-y; }          

In this example, I've simply added a background-color and a background-image that tiles vertically. This will update the styles for the bars and the little icons that stand for them in the legend. Overnice.

And, believe it or not, that is it!

The Finished Product

screenshot

That virtually wraps it upwardly. I promise nosotros've washed enough to alert the public to the dangers of zombie over-population. More than that, however, I hope you lot've gained something useful from this tutorial and that you'll go along to button the boundaries of what can be done in the browser — especially with proper Web standards and without the use of third-party plug-ins. If yous've got ideas on how to extend or amend annihilation you've seen here, don't hesitate to leave a annotate below, or find me on Twitter @derek_mack.

Bonus: Unleashing The Power Of CSS3

This bonus is not as detailed as our primary example. Information technology serves mainly as a showcase of some features being considered in the CSS3 specification.

Considering back up for CSS3 backdrop is currently limited, so is their apply. Although some of the features mentioned here are making their way into other Web browsers, Webkit-based ones such as Apple Safari and Google Chrome are leading the fashion.

We can actually create our graph using no images at all, and even animate the bars using CSS instead of jQuery.

Let'southward commencement by removing the background images from our bars, replacing them with the -webkit-slope property:

            .fig0 {    background: -webkit-gradient(linear, left top, right top, color-stop(0.0, #747474), color-cease(0.49, #676767), color-stop(0.five, #505050), color-terminate(1.0, #414141)); }  .fig1 {    background: -webkit-gradient(linear, left top, correct top, color-end(0.0, #65c2e8), color-finish(0.49, #55b3e1), color-stop(0.5, #3ba6dc), colour-cease(1.0, #2794d4)); }  .fig2 {    groundwork: -webkit-gradient(linear, left superlative, correct top, colour-stop(0.0, #eea151), color-finish(0.49, #ea8f44), color-stop(0.5, #e67e28), colour-stop(ane.0, #e06818)); }          

Nosotros can practise the same with our little number pop-ups:

            .bar span {    background: -webkit-gradient(linear, left peak, left bottom, color-terminate(0.0, #fff), color-stop(one.0, #e5e5e5));    … }          

For more information on Webkit gradients, check out the Surfin' Safari blog.

Continuing with the pop-ups, let's introduce -webkit-transition. CSS transitions are remarkably piece of cake to utilize and sympathize. When the browser detects a alter in an chemical element'south property (height, width, color, opacity, etc.), information technology volition transition to the new holding.

Again, refer to Surfin' Safari for more than information on -webkit-transition and CSS3 blitheness.

Hither's an instance:

            .bar span {    background: -webkit-gradient(linear, left acme, left bottom, color-cease(0.0, #fff), color-stop(1.0, #e5e5e5));    display: block;    opacity: 0;     -webkit-transition: all 0.2s ease-out; }  .bar:hover span {    opacity: one; }          

When you hover over the bar, the margin and opacity of the pop-upwardly will alter. This triggers a transition event co-ordinate to the backdrop we have set. Very cool.

Cheers to -webkit-transition, we can simplify our JavaScript functions a scrap:

            // Set individual height of bars role displayGraph(bars, i) {    // Changed the way we loop because of issues with $.each not resetting properly    if (i < bars.length) {       // Add transition backdrop and set superlative via CSS       $(bars[i].bar).css({'meridian': bars[i].height, '-webkit-transition': 'all 0.8s ease-out'});       // Look the specified time, so run the displayGraph() role again for the next bar       barTimer = setTimeout(office() {          i++;          displayGraph(confined, i);       }, 100);    } } // Reset graph settings and prepare for display function resetGraph() {    // Set bar height to 0 and clear all transitions    $.each(bars, function(i) {              $(bars[i].bar).stop().css({'height': 0, '-webkit-transition': 'none'});              });     // Articulate timers    clearTimeout(barTimer);    clearTimeout(graphTimer);     // Restart timer    graphTimer = setTimeout(role() {       displayGraph(bars, 0);    }, 200); }          

Here are the main things we've changed:

  • Prepare the height of the bars via the jQuery css() function, and immune CSS transitions to accept care of the animation;
  • When resetting the graph, turned transitions off so that the superlative of the confined is instantly prepare to 0.

Check out the example if you take the latest version of Safari or Chrome installed.

Ultra-Mega Webkit Bonus: At present In 3-D!

For a sneak peek of what the future holds, check out a little experiment that I put together, with a three-D effect and CSS transforms. Again, it requires the latest versions of Safari or Chrome:

As in our previous Webkit example, there are no images, and all blitheness is handled via CSS. Kiss my face up!

I tin't tell y'all what to do with all this information. But I do caution you lot most the potential misuse of your new powers. In the words of our friend Captain Planet, "The power is yours!"

Use it wisely.

Smashing Editorial (al, kw, il)

Source: https://www.smashingmagazine.com/2011/09/create-an-animated-bar-graph-with-html-css-and-jquery/

Posted by: hookcounces.blogspot.com

0 Response to "How To Make An Animated Bar Graph Css"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel