Styling a group of checkboxes as a dropdown via CSS and JavaScript

The following demonstrates how to create a dropdown list based on a list of checkbox elements. You can view a complete example of how this works at the following fiddle: http://jsfiddle.net/Terkildsen/mTSLa/

In the following I will walk you through the code.

First, I start by creating the HTML:

    <div class="dropdown">
        Choose city
        <ul class="dropdown-list">
            <li>
                <label>
                    <input type="checkbox" value="Vejle" name="city" />Vejle</label></li>
            <li>
                <label>
                    <input type="checkbox" value="Horsens" name="city" />Horsens</label></li>
            <li>
                <label>
                    <input type="checkbox" value="Kolding" name="city" />Kolding</label></li>
            <li>
                <label>
                    <input type="checkbox" value="Fredericia" name="city" />Fredericia</label></li>
        </ul>
    </div>

There’s not much to say about the HTML above. I use a “dropdown” div to contain the entire dropdown – including the default text that is displayed when the checkbox list is hidden. I then assign a “dropdown-list” class to the list of checkboxes.

Then, I start styling the dropdown:

.dropdown {
    width: 200px;
    border: 1px solid silver;
    cursor: pointer; /* use correct mouse pointer when hovering over the dropdown */
    padding: 10px;
    position: relative;
    margin: 0 auto;
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    -khtml-user-select: none;
    -moz-user-select: none;
    -ms-user-select: none;
    user-select: none;
}

In the CSS above I start by styling the entire dropdown. I provide the dropdown with a width of 200px, some padding and a border to make the dropdown resemble a normal select-list.

Notice that the usage of the “user-select: none;” prevents the user from accidentally highlighting the text in the dropdown.

/* Display CSS arrow to the right of the dropdown text */
.dropdown:after {
    content:'';
    height: 0;
    position: absolute;
    width: 0;
    border: 6px solid transparent;
    border-top-color: #000;
    top: 50%;
    right: 10px;
    margin-top: -3px;
}

Instead of inserting an image to display an arrow in order to indicate that this dropdown can be clicked on, I use a small CSS-trick that will display an arrow using a border-property. You can read more about how this is done in the following blogpost: https://pterkildsen.com/2012/09/16/creating-arrows-using-css/

/* Reverse the CSS arrow when the dropdown is active */
.dropdown.is-active:after {
    border-bottom-color: #000;
    border-top-color: #fff;
    margin-top: -9px;
}

When the user activates the dropdown, the CSS above reverses the direction of the CSS-arrow. It does this by applying the white background-color to the “border-top-color”-property so that this part of the border is hidden, and then displays the bottom part of the border by assigning a black color to the “border-bottom-color”-property.

.dropdown-list {
    list-style: none;
    margin: 0;
    padding: 0;
    position: absolute;
    top: 100%; /* align the dropdown right below the dropdown text */
    border: inherit;
    border-top: none;
    left: -1px; /* align the dropdown to the left */
    right: -1px; /* align the dropdown to the right */
    opacity: 0; /* hide the dropdown */
    -webkit-transition: opacity 0.4s ease-in-out;
    -moz-transition: opacity 0.4s ease-in-out;
    -o-transition: opacity 0.4s ease-in-out;
    -ms-transition: opacity 0.4s ease-in-out;
    transition: opacity 0.4s ease-in-out;
    pointer-events: none; /* avoid mouse click events inside the dropdown */
}

I then style the list of checkboxes. There are a couple of important issues here. First of all, I use the “opacity”-property to hide and display the checkbox-list. And secondly, I use the “pointer-events: none” to prevent click events from happening in the checkbox-list – which would accidentally hide it.

.is-active .dropdown-list {
    opacity: 1; /* display the dropdown */
    pointer-events: auto; /* make sure that the user still can select checkboxes */
}

The is-active class is applied to the checkbox-list whenever the user clicks on the “dropdown-selector”. This class displays the checkbox-list by setting the “opacity”-property to 1 and it also makes sure that the user still can select the checkboxes by setting “pointer-events: auto;”

.dropdown-list li label {
    display: block;
    border-bottom: 1px solid silver;
    padding: 10px;
    -webkit-transition: all 0.2s ease-out;
    -moz-transition: all 0.2s ease-out;
    -o-transition: all 0.2s ease-out;
    -ms-transition: all 0.2s ease-out;
    transition: all 0.2s ease-out;
}

.dropdown-list li label:hover {
    background-color: #c41230;
    color: white;
}

And then finally, I just add some finishing touches to the labels inside the checkbox-list.

In order for this to work, I add a couple of lines of jQuery:

         $(function () {
            $(".dropdown").click(function () {
                $(this).toggleClass("is-active");
            });

            $(".dropdown ul").click(function (e) {
                e.stopPropagation();
            });
        });

The code above detects the click event on the “dropdown”-div – which will toggle the “is-active” class. It also captures the click-event on the list of checkboxes in order to make sure that this doesn’t accidentally close the dropdown.

This code has been tested in Internet Explorer 9+, the latest version of Mozilla Firefox and Google Chrome. I have also tested it on iPhone and iPad. Seems to be working just fine. However, please notice that I’ve had some issues with jsfiddle and Internet Explorer. The code works fine without jsfiddle, though.

Trigger HTML5 form validation onblur using jQuery and checkValidity

HTML5 form validation is in my view one of the nicest things in HTML5. It makes it very easy to implement form validation, and you avoid writing your own validator or implementing some custom jQuery-plugin – which improves load time and reduces complexity of the solution.

As you probably know, the HTML5 form validation works by adding different attributes to the input element. The code below will make sure that the user only inserts a number in the range of 1000 to 9999. The user will not be able to submit the form unless the correct number has been inserted.

<input type="text" name="zipcode" placeholder="Insert zip-code" class="zipcode" min="1000" max="9999" maxlength="4" size="4" required="required">

However, this validation is only triggered when the user tries to submit a form. Clicking submit will produce an error message, and nothing will be sent to the server. I had a certain case where I needed to validate each input-field onblur.

In this case, you can use the checkValidity-method in the constraint validation API. (You can read more about this API in the following blogpost: http://www.html5rocks.com/en/tutorials/forms/constraintvalidation/). The checkValidity-method can be applied to each input-element and will return true if the value of the input-element is valid. Otherwise, it will return false.

I therefore ended up applying the following small jQuery-snippet in order to add form validation onblur:

$('#wizardvalidator input').blur(function(e) {
	var controlgroup = $(e.target.parentNode);
	if (!e.target.checkValidity()) {
		controlgroup.removeClass('success').addClass('error');
	} else {
		controlgroup.removeClass('error').addClass('success');
	}
});

The code above checks each and every input-element onblur and adds the necessary classes in order to display a validation-state to the user.

Create a HTML5 canvas element with clickable elements

Canvas with clickable circles
A client asked me to implement a website with different images having clickable circles on top of them. The circles would be placed randomly – and would be used to display additional information about what was going on in that particular place in the image.

Fortunately, the client did not require that users with older browser should be able to view the website. So I was able to use some modern technology in order to make my life a bit easier.

I have never worked with the HTML5 canvas-element before. So I needed to get some hands-on experience with it. I therefore decided to create a proof-of-concept first. You can see this proof-of-concept in the following fiddle: http://jsfiddle.net/Terkildsen/9pgWf/

The following is a quick walk-through of the elements in the code. First, I start off with a canvas element:

  <body>
    <canvas id="myCanvas" width="1420" height="702"></canvas>
  </body>

After that, I create a reference to the canvas-element in my JavaScript – and initialize a circles-array that will hold information about each circle:

var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d');
var circles = [];

Then I implement a function to handle the drawing of each circle. The function will accept a reference to the canvas-element (context), the x- and y-position of the circle, the fillcolor, radius, and different other parameters necessary to draw the circle. The circle is drawn by using the context.arc-method – which is documented right here: http://www.html5canvastutorials.com/tutorials/html5-canvas-arcs/

var draw = function (context, x, y, fillcolor, radius, linewidth, strokestyle, fontcolor, textalign, fonttype, filltext) {
    context.beginPath();
    context.arc(x, y, radius, 0, 2 * Math.PI, false);
    context.fillStyle = fillcolor;
    context.fill();
    context.lineWidth = linewidth;
    context.strokeStyle = strokestyle;
    context.stroke();
    
    context.fillStyle = fontcolor;
    context.textAlign = textalign;
    context.font = fonttype;
    
    context.fillText(filltext, x, y);    
};

Now I create an object that will hold the position of each circle – this information is used later on for deciding whether or not the user clicked on the circle

var Circle = function(x, y, radius) {
    this.left = x - radius;
    this.top = y - radius;
    this.right = x + radius;
    this.bottom = y + radius;
};

I then create a drawCircle-method that will call the draw-method – in order to draw the circle – and that will store the position of each new circle.

var drawCircle = function (context, x, y, fillcolor, radius, linewidth, strokestyle, fontcolor, textalign, fonttype, filltext, circles) {
    draw(context, x, y, fillcolor, radius, linewidth, strokestyle, fontcolor, textalign, fonttype, filltext);
    var circle = new Circle(x, y, radius);
    circles.push(circle);  
};

Now I can add my circles – naturally, this would come from a database containing the texts as well as the coordinates of each circle.

drawCircle(context, 300, canvas.height / 2, "green", 40, 5, "#003300", "white", "center", "bold 32px Arial", "1", circles);
drawCircle(context, 600, canvas.height / 3, "blue", 50, 5, "#003300", "white", "center", "bold 32px Arial", "2", circles);

And then, finally, I add a jQuery-method that will bind to the click-event, and decide which circle – if any – the user clicked. Here, I use the previously stored information about each circles’ position to calculate whether or not the user clicked a certain circle. Right now, I just alert some dummy text – but I will add a method to display additional information.

$('#myCanvas').click(function (e) {
    var clickedX = e.pageX - this.offsetLeft;
    var clickedY = e.pageY - this.offsetTop;
    
    for (var i = 0; i < circles.length; i++) {
        if (clickedX < circles[i].right && clickedX > circles[i].left && clickedY > circles[i].top && clickedY < circles[i].bottom) {
            alert ('clicked number ' + (i + 1));
        }
    }
});

That’s it – for a complete working sample please go to http://jsfiddle.net/Terkildsen/9pgWf/

Executing code when all templates have been included – AngularJS

Sometimes, you want to be able to execute code when all the templates have been included in AngularJS using the ng-include attribute. Perhaps, you want to manipulate the inserted HTML…

AngularJS doesn’t provide a method to handle this out of the box, so instead you have to implement it yourself. This is done by utilizing the $includeContentLoaded event, which is fired each time one template has been loaded. Just like the following codesnippet:

scope.$on('$includeContentLoaded', function(event) {
    renderedcount++;
    if (renderedcount == itemmax) {
        // Crappy hack!
        $('.removeparent').unwrap();
        $('.removeparent').removeClass('removeparent');
    }
    console.log('another include was loaded', event.targetScope);
});

Don’t mind the “Crappy hack”-comment…This is just me telling myself, that I should probably refactor this code at some point…*ahem*….

But anyway, you subscribe to the $includeContentLoaded-event, and keep track of all the templates by using a simple counter. When all templates are loaded, you execute the code – which in this instance is jQuery to manipulate the inserted HTML.

It would be nice to have this feature in AngularJS itself – but unfortunately it isn’t. But this way, you can still get what you want…

Creating pure CSS-based tabs

I know! There are so many jQuery-plugins out there, that enable you to create dynamic tabs-functionality. One of the most widely used is probably jQuery UI’s tab-plugin (http://jqueryui.com/demos/tabs/). So why bother building a new one?

Well, first of all I’m experimenting a lot with CSS right now, and secondly, I wanted to build a plugin that didn’t utilize any JavaScript at all. Don’t get me wrong – I use jQuery all the time, and I’m a big fan of it. But I want to create UI’s that download and respond extremely fast. Adding one or two jQuery plugins to a page normally doesn’t present a problem, but when you’re working with very rich and dynamic user interfaces, you often need to add several different plugins. Which means that the user will have to download lots of files, and the browser needs to perform a lot of rendering. If you can replace JavaScript with CSS, you can improve performance of the website, and you will improve the user experience.

I searched for a solution to this problem, and I found that the CSS :target pseudo-class could help me out. It is a part of the CSS3-specification and it enables you to create CSS that will respond to the part of a URL that comes after a “#”. You could – for example – create CSS that would respond to the “tab1” in the following URL: https://pterkildsen.com/2012/09/24/creating-pure-css-based-tabs#tab1.

I could write HTML like this:

<section id="tab1">
<h2>
                    <a href="#tab1">Tab 1</a></h2>
                <img src="Images/blue river scenery wallpapers.jpg" alt="" /></section>

And then CSS like this:

section:target h2
{
    color: #FFF;
    background-color: #e21414;
}

The code above will change the font- and the background color of the section with the id of “tab1” when a user enters the URL https://pterkildsen.com/2012/09/24/creating-pure-css-based-tabs#tab1.

This little trick makes it very easy for us to create tabs-functionality. I have provided a small example in the following:

The HTML:

<article class="tabs"><section id="tab1">
<h2>
                    <a href="#tab1">Tab 1</a></h2>
                <img src="Images/blue river scenery wallpapers.jpg" alt="" /></section><section id="tab2">
<h2>
                <a href="#tab2">Tab 2</a></h2>
                <img src="Images/farnsworth-landscaping-firebowl5.jpg" alt="" /></section><section id="tab3">
<h2>
                <a href="#tab3">Tab 3</a></h2>
                <img src="Images/Moonlit_Landscape.jpg" alt="" /></section><section id="tab4">
<h2>
                <a href="#tab4">Tab 4</a></h2>
                
</section><section id="tab5"> <h2> <a href="#tab5">Tab 5</a></h2> <img src="Images/Moonlit_Landscape.jpg" alt="" /></section> </article>

And the CSS:

article.tabs section:target h2
{
    color: #FFF;
    background-color: #e21414;
    background: #F15931;
    background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPgogICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2YxNTkzMSIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNkNDM0MGEiIHN0b3Atb3BhY2l0eT0iMSIvPgogIDwvbGluZWFyR3JhZGllbnQ+CiAgPHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEiIGhlaWdodD0iMSIgZmlsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz4KPC9zdmc+);
    background: -moz-linear-gradient(top,  #F15931 0%, #D4340A 100%); /* FF3.6+ */
    background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#F15931), color-stop(100%,#D4340A)); /* Chrome,Safari4+ */
    background: -webkit-linear-gradient(top,  #F15931 0%,#D4340A 100%); /* Chrome10+,Safari5.1+ */
    background: -o-linear-gradient(top,  #F15931 0%,#D4340A 100%); /* Opera 11.10+ */
    background: -ms-linear-gradient(top,  #F15931 0%,#D4340A 100%); /* IE10+ */
    background: linear-gradient(to bottom,  #F15931 0%,#D4340A 100%); /* W3C */
    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#F15931', endColorstr='#D4340A',GradientType=0 ); /* IE6-8 */
}

article.tabs section h2
{
    color: #F15B33;
    background: #eeeeee;
    background: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/Pgo8c3ZnIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgdmlld0JveD0iMCAwIDEgMSIgcHJlc2VydmVBc3BlY3RSYXRpbz0ibm9uZSI+CiAgPGxpbmVhckdyYWRpZW50IGlkPSJncmFkLXVjZ2ctZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPgogICAgPHN0b3Agb2Zmc2V0PSIwJSIgc3RvcC1jb2xvcj0iI2VlZWVlZSIgc3RvcC1vcGFjaXR5PSIxIi8+CiAgICA8c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiNjY2NjY2MiIHN0b3Atb3BhY2l0eT0iMSIvPgogIDwvbGluZWFyR3JhZGllbnQ+CiAgPHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEiIGhlaWdodD0iMSIgZmlsbD0idXJsKCNncmFkLXVjZ2ctZ2VuZXJhdGVkKSIgLz4KPC9zdmc+);
    background: -moz-linear-gradient(top,  #eeeeee 0%, #cccccc 100%); /* FF3.6+ */
    background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#eeeeee), color-stop(100%,#cccccc)); /* Chrome,Safari4+ */
    background: -webkit-linear-gradient(top,  #eeeeee 0%,#cccccc 100%); /* Chrome10+,Safari5.1+ */
    background: -o-linear-gradient(top,  #eeeeee 0%,#cccccc 100%); /* Opera 11.10+ */
    background: -ms-linear-gradient(top,  #eeeeee 0%,#cccccc 100%); /* IE10+ */
    background: linear-gradient(to bottom,  #eeeeee 0%,#cccccc 100%); /* W3C */
    filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#eeeeee', endColorstr='#cccccc',GradientType=0 ); /* IE6-8 */
}

/* CSS to handle images and videoes in tabs content */
article.tabs section:target p
{
    opacity: 1;
}

article.tabs section p
{
    opacity: 0;
    -webkit-transition: opacity 1s ease-in-out;
    -moz-transition: opacity 1s ease-in-out;
    -o-transition: opacity 1s ease-in-out;
    -ms-transition: opacity 1s ease-in-out;
    transition: opacity 1s ease-in-out;
}

The code mentioned above is very simple. It really defines two different states for the same HTML-elements, and the matching will be based on the id of the HTML-element and the part of the URL that is after the “#”. Therefore, you can create an “active” state and a “passive” state – defined by the :target-class.

Of course, there is a lot more to the tabs-functionality than just this. But this is the most important part – the rest is primarily styling and layout. However, there are two more important things that you need to be aware of.

First of all, if you just implement the code mentioned above, you will run into a problem. You probably don’t want your users to type a difficult URL consisting of hash tags – you want the URL to be as simple as possible. But that would mean that the CSS wouldn’t be able to display the active tab – as it would have no information as to what should be the active tab.

Therefore, you need to inform the CSS by using the following JavaScript, that will append the necessary information to the URL (yeah, I know….it’s JavaScript….):

    <script type="text/javascript">
        window.location.hash = '#tab1'
    </script>

Secondly, this tabs-functionality will not work in Internet Explorer 7 and 8. I have decided that I won’t optimize for Internet Explorer 7 and I’m waiting for the day when I can declare proudly that I won’t optimize for IE8 either. Meanwhile, there’s a solution in the form of a JavaScript-library called Selectivizr (http://selectivizr.com/). This library enables CSS3 pseudo-classes in IE7 and IE8 – however, the :target pseudo-class in Selectivizr only works in IE8.
I know that using this library will add additional JavaScript-overhead to your project – however, you can isolate the problem to those sad people using Internet Explorer by inserting the following lines of code:

<!--[if (gte IE 6)&(lte IE 8)]>
  <script type="text/javascript" src="selectivizr.js"></script>
<![endif]-->

That’s it! This new world of CSS3 continues to amaze me. ūüôā

UPDATE! Why you can’t just use plain CSS!

So, I encountered one very difficult obstacle trying to implement the code above into a real life scenario. The problem is that the code above works just fine – as long as the tabs are placed at the top of the page! If you don’t place the tabs at the top, you will observe a scrolling behaviour whenever you click each tab. Not pretty, but still the default behaviour of the a tag. So nothing odd there.

I tried several approaches in order to solve this problem. One possible approach was to prevent the default behaviour of the a-tag whenever the user clicked a tab – but to no success. I therefore ended up implementing a small jQuery JavaScript to make it work. I know that this kinda ruins the idea of implementing a ‘pure’ CSS3 based tabs functionality, but I simply wasn’t able to accomplish it otherwise. I use this blog to document my findings and this time I learned that it’s not possible to create a pure CSS-based tabs-functionality without scroll. If you know how to do it, please don’t hesitate to let me know – for now, I think that the JavaScript below is a very small JavaScript-footprint.

Here’s the code for the jQuery-snippet:

    <script type="text/javascript">
      $(function() {
        $('article.tabs section div h2 a').click(function(e) {
          $('article.tabs li').removeClass("target");
          $(this).parent().parent().parent().addClass("target");
          e.preventDefault();
        });
      });
    </script>

Also, please read my new findings about handling Youtube-videos in tabs: https://pterkildsen.com/2012/09/22/fade-one-image-into-another-via-css/

Inds√¶t diagrammer p√• din Dynamicweb hjemmeside

Vi mennesker har generelt nemmere ved at forstå komplekse data, hvis disse data bliver visualiseret i form af eksempelvis diagrammer. De fleste mennesker kan hurtigere konkludere på baggrund af visualiseringer, end de kan på baggrund af komplicerede tabeller med data.

Men med de nuv√¶rende teknologier er det ofte sv√¶rt at pr√¶sentere flotte diagrammer p√• en hjemmeside. Derfor ser man, at disse grafer¬† og diagrammer bliver gemt v√¶k i et PDF-dokument, der f√łrst skal downloades til brugerens computer. Eller ogs√• er de indsat som gif-billeder p√• siden. Alternativt – og meget sj√¶ldent – bliver diagrammerne genereret ved hj√¶lp af Flash, Silverlight eller andre propriet√¶re teknologier. Og oftest ender man med at give op, og i stedet pr√¶sentere data i form af en tabel.

Generelt g√łr de nuv√¶rende teknologier det rigtig sv√¶rt at lave grafer, som er tilg√¶ngelige for alle brugere. Uanset om man pr√¶senterer grafer i form af et eksternt PDF-dokument, eller i form af eksempelvis Flash, s√• st√•r man med et tilg√¶ngelighedsproblem. Mange mennesker vil undlade at downloade PDF-dokumentet og synshandicappede brugere, som anvender sk√¶rml√¶sere, vil ikke kunne afkode Flash-scriptet. Derfor er disse teknikker i praksis ikke anvendelige p√• offentlige hjemmesider – som paradoksalt er de hjemmesider, der oftest har behov for at pr√¶sentere store m√¶ngder af data.

HTML5

Dette er noget, som kan l√łses ved hj√¶lp af HTML5 og jQuery. (L√¶s mere om HTML5 her: http://da.wikipedia.org/wiki/HTML5). HTML5 indeholder et s√•kaldt canvas-element, som giver mulighed for dynamisk at generere bitmap grafik. Kombineret med JavaScript-biblioteket jQuery giver det mulighed for at generere diagrammer on-the-fly. Filament Group har udviklet et jQuery-plugin, som er i stand til at tage data fra en almindelig HTML-tabel, og skabe en graf ud fra disse data. (L√¶s mere om dette plugin her: http://www.filamentgroup.com/lab/update_to_jquery_visualize_accessible_charts_with_html5_from_designing_with/)

Dette er en rigtig smart teknik, fordi det giver mulighed for både at præsentere data i tabelform, og automatisk præsentere data ved hjælp af grafer. Teknikken giver flere fordele:

  1. Tilgængelighed
    Man sikrer tilgængeligheden Рi forhold til overholdelse af W3C og WCAG AA Рved at data både eksisterer i form af en tabel og i form af grafik.
  2. Automatisering
    Redakt√łren skal kun fokusere p√• at inds√¶tte en simpel tabel indeholdende data. Teknikken vil derefter s√łrge for at konvertere data automatisk.
  3. Brugervenlighed
    Man g√łr hjemmesiden mere brugervenlig og tilg√¶ngelig overfor de brugere, som kan se og anvende grafen. Det bliver nemmere og hurtigere at overskue data.

Dynamicweb og grafer

Dynamicweb indeholder muligheden for at inds√¶tte tabeller. Og efterh√•nden overholder disse tabeller ogs√• tilg√¶ngelighedskriterierne. Det g√łr det derfor muligt at inds√¶tte tabeller, som rent faktisk validerer, og som derfor ogs√• er anvendelige p√• offentlige hjemmesider.

Vi har i Bleau udviklet et lille modul, som kan indsættes på et afsnit, og som kan konvertere data fra en tabel til en graf Рnaturligvis ved hjælp af ovenstående plugin.

Et eksempel på hvordan data fra tabeller kan konverteres til graferI ovenstående billede ses et screenshot af en simpel demo indeholdende en tabel med salgstal på forskellige varegrupper. Tabellen er lavet som et helt almindeligt Dynamicweb-afsnit, og ved hjælp af en simpel tabel.

Systemet s√łrger derefter for automatisk at generere en graf ud fra de data, der er tilg√¶ngelige i tabellen. Kolonne- og r√¶kkeoverskrifter identificeres automatisk – hvilket dog naturligvis kr√¶ver, at disse er korrekt indsat i tabellen. Kolonne- og r√¶kkeoverskrifter anvendes herefter til at generere de labels, der anvendes i grafen.

Hvis der er en overskrift på tabellen, så indsættes denne overskrift ligeledes som overskrift i grafen. Tabellen bibeholdes naturligvis på siden, så synshandicappede brugere stadig kan anvende siden.

Det skal understreges, at den tabel, som indsættes i afsnittet, skal være valid. Det betyder, at det er en rigtig dårlig ide at indsætte en tabel fra eksempelvis Word, da en sådan tabel indeholder en masse HTML-kode, som ikke er valid. Grafen vil ikke blive genereret eller vil komme til at se underlig ud.

Administration

Som sagt er modulet s√¶rdeles simpelt, og det eneste, vi g√łr er, at formidle data fra Dynamicweb til et jQuery-plugin. I modulet er der en lang r√¶kke indstillinger, som man kan v√¶lge at s√¶tte – eller ogs√• kan man v√¶lge at l√¶ne sig op af standardv√¶rdierne.

Afsnitsindstillinger Bleau Charts

Indstillingerne giver mulighed for at konfigurere en lang r√¶kke parametre – eksempelvis h√łjde og bredde – og de fleste af disse parametre har karakter af at v√¶re orienterede imod at styre udseendet af grafen. Bem√¶rk ogs√•, at man kan v√¶lge mellem fire forskellige typer af grafer:

  • S√łjlediagram
  • Lagkagediagram
  • Linjediagram
  • Kurvediagram

Alle indstillinger kan overstyres ved hj√¶lp af den medf√łlgende template-fil, og de medf√łlgende CSS-filer. Her kan man desuden udbygge funktionaliteten, hvis man har lyst til det.

Browsere

HTML5 Canvas-elementet er underst√łttet i alle moderne browsere. Det vil sige, at hvis du anvender seneste generation af Mozilla, Chrome, Opera, Safari og Internet Explorer, s√• kan din browser vise grafik ved hj√¶lp af HTML5-teknologien. Anvender du en √¶ldre version af Internet Explorer, s√• vil canvas-elementet ikke fungere. Da en hel del brugere – desv√¶rre – stadig anvender √¶ldre versioner af Internet Explorer, s√• vil vi dermed st√• med den udfordring, at disse brugere ikke vil kunne se grafen.

Men det er i virkeligheden ikke et problem. Den teknik, der anvendes til at skabe graferne med, tager selv hensyn til hvilken browser, der anvendes. Og anvendes der en √¶ldre browser, s√• vises kun tabellen – og ingen graf. De brugere, som stadig render rundt i oldtiden, straffes dermed ved, at de ikke kan se den fantastiske nye verden. ūüôā

S√•dan tvinger du brugeren til at v√¶lge bestemte dage i en jQuery datepicker

Ja, overskriften siger jo det hele.:-)

Jeg havde brug for en datepicker-funktion, som kun gav brugeren mulighed for at vælge enten den 1. eller den 15. i en måned. Det viste sig at være ganske simpelt:

Sådan bruger du jquery til at vælge bestemte datoer i en datepicker

Bem√¶rk “beforeShowDay”, som giver mulighed for at skyde en funktion ind f√łr en dato renderes. Her formaterer jeg datoen til en streng, og f√•r et tal ud, som jeg s√• blot sammenligner med de datoer, som m√• anvendes.

Det er da nemt!

Brug de officielle adressewebservices til at validere brugerens indtastning

Erhvervs- og Byggestyrelsen har lanceret en r√¶kke adresse webservices, som kan benyttes til at sl√• adresseoplysninger op med. De mange services er gratis at bruge, og vil kunne finde anvendelse i rigtig mange applikationer. Form√•let er at h√łjne kvaliteten af adressedata i forskellige applikationer igennem en central vedligeholdt adressedatabase.

Et eksempel kunne være et check for om en bruger indtaster korrekte adresseoplysninger i en webformular. Et andet eksempel kunne være automatisk valg af by på baggrund af postnummer.

Jeg har pr√łvet at bruge de gratis adresse webservices til at programmere de to ovenst√•ende scenarier. I det ene scenarie anvender jeg JQuery autosuggest til at fors√łge at lave en www.rejseplanen.dk funktion, som er i stand til at sl√• adressen op efterh√•nden som brugeren indtaster. Det fungerer desv√¶rre ikke helt vildt godt, da de nuv√¶rende metoder i webservicen ikke benytter wildcard-s√łgning, men i stedet en slags fuzzy logic s√łgning, som er i stand til at finde en adresse ud fra forskellige stavem√•der.En anden √•rsag er, at da der er tale om gratis webservices, s√• g√•r det temmelig langsomt. P√• min egen udviklingsmaskine oplevede jeg, at visse foresp√łrgsler kunne tage 12 ‚Äď 15 sekunder.

Men i slutningen af april m√•ned kommer der en ny opdateret version. Denne vil indeholde wildcard-s√łgning, og serverparken bliver opgraderet. Og s√• vil det v√¶re rigtig anvendeligt.

Jeg har vedh√¶ftet kildekoden til denne blogpost, og s√• kan du selv pr√łve dig frem. Kort fortalt g√•r det ud p√• f√łlgende:

image

Ovenst√•ende kode finder et bynavn p√• baggrund af et postnummer eller en del af et postnummer. ‚Äė71‚Äô vil derfor finde b√•de ‚Äė7100‚Äô og ‚Äė7120‚Äô, osv. Jeg starter med at etablere en adresseservice via GetAddressService, laver derefter min request via GetPostCodeByPostCodeIdentifierRequestType og eksekverer til sidst min foresp√łrgsel via addressService.GetPostCodeByPostCodeIdentifier.

P√• grund af webservicens d√•rlige performance, s√• anvender jeg caching. Det betyder, at den f√łrste foresp√łrgsel er temmelig langsom, men de efterf√łlgende foresp√łrgsler g√•r hurtigt.

image

Dernæst har jeg lavet en simpel HTML-side, som indeholder to felter til henholdsvis postnummer og by. Via JQuery Autocomplete slår jeg op, får et resultat tilbage, og når brugeren vælger den rigtige by, så fordeler jeg værdierne ud på henholdsvis postnummer og by.

‚ÄúRejseplanen.dk‚ÄĚ-funktionen er ogs√• med i vedh√¶ftede kildekode.

Du kan læse meget mere om de officielle adressewebservices her: http://www.ebst.dk/aws

Du finder mere information og nyheder her: http://digitaliser.dk/group/334445

Dokumentationen finder du her: http://digitaliser.dk/resource/334598/artefact/1+Systembeskrivelse.pdf

Og endelig kan du downloade min kildekode her: http://cid-c352fc667ec78374.skydrive.live.com/self.aspx/aws/aws.7z