Simple numbered markers with Leaflet.js

I thought I’d look up how to create markers that are numbered with the excellent Leaflet.js. Perhaps surprisingly there is no built in method, but looking round the Web there are a few suggested ways of doing it. These all seemed rather over-engineered though so I decided to engage brain rather than just blindly copying. The following method works, and is a lot less complicated than anything else I’ve seen out there.

I wanted to achieve this (yes I know grey is not a good choice in this case, but it is what the user wanted):
map

To get markers like this first of all copy the Leaflet.js default marker image, which in 0.7.3 is here -> http://cdn.leafletjs.com/leaflet-0.7.3/images/marker-icon.png . I simply modified this using Paint.Net to be solid grey. Put the final image into your website/application.

Create a CSS class like this, obviously substituting your image path/name and font colour:

.number-icon
{
	background-image: url("images/number-marker-icon.png");
	text-align:center;
	color:White;	
}

Then when creating your marker you need to add this code, putting the number in the ‘html’ parameter:

      var numberIcon = L.divIcon({
            className: "number-icon",
            iconSize: [25, 41],
            iconAnchor: [10, 44],
            popupAnchor: [3, -40],
            html: variable_containing_the_number        
      });

      var marker = new L.marker([lat, long],
                            {
                                
                                icon: numberIcon
                                
                            });

And that’s all there is to it. Not quite sure why there are so many other elaborate methods out there, but maybe they work for use cases other than the one I had too.

IE8 clips the text from a max-widthed drop-down box – fix!

OK, Windows XP only has about 6 months to live from the date of this post, and so IE8 (which is XP only) will die out sooner rather than later, but if you are in the unfortunate position of having to try and support ancient browsers then you will have almost certainly come across the problem with fixed-width or max-width drop-down lists. If the width of your options exceeds the width (or max width) of the drop-down box then IE8 just clips the text, which may well mean that your users can’t read it. Another useful feature from IE.

There is a suggested solution here for fixed width drop-downs: http://jquerybyexample.blogspot.com/2012/05/fix-for-ie-select-dropdown-with-fixed.html, which is useful however does not quite address the problem of drop-down boxes with no fixed width defined, but which do have a max-width defined.

The solution is a modification of the above link. Firstly wrap your drop down in a div with a width the same as the max-width of the drop-down, and an overflow value of hidden. The example below shows a Razor ASP.NET drop down, but this will work with HTML too.

  <div style="width:400px;overflow:hidden;">
            @Html.DropDownList("Selection", Model.ChoiceList, new { id = "ddlChoose", @class="ddlClass", style = "max-width:400px; text-overflow: ellipses; " }) 
  </div>

Then in the jQuery $(document).ready() method put the following code on the change, blur, mousedown and mouseup events for your drop-down. In this example I’ve done the element selection by class, but obviously you can use ID if you want. I have used the jQuery ‘live’ method as my drop-downs were not necessarily generated on page load, however you can just use the mousedown() etc methods if you are not doing anything fancy.

$(document).ready(function () {

    /* Begin functions for IE8 drop-down boxes */

    $(".ddlClass").live("mousedown", function () {
        SetToAutoWidth($(this), true, 400);
    });

    $(".ddlClass").live("change", function () {
       ResetToFixedWidth($(this), true, 400);
    });
    $(".ddlClass").live("blur", function () {
       ResetToFixedWidth($(this), true, 400);
    });

    $(".ddlClass").live("mouseup", function () {
       ResetToFixedWidth($(this), true, 400);
   });

  
    /* End functions for IE8 drop-down boxes */

});

This final bit of jQuery contains the methods that actually do the work. Note that you could probably make the FindTextWidth() method more efficient by only running it on page load, and persisting the div (as mentioned in the stackoverflow reply I took this from).

// Return the width (in pixels) of the passed string
// From here: http://stackoverflow.com/questions/118241/calculate-text-width-with-javascript and slightly modified
function FindTextWidth(textToMeasure, fontFamily, fontSize){
    var o = $('<div>' + textToMeasure + '</div>').css({ 'position': 'absolute', 'float': 'left', 'white-space': 'nowrap', 'visibility': 'hidden', 'font-family': fontFamily, 'font-size': fontSize }).appendTo($('body'));
    var w = o.width();

    o.remove();
    return w;
}

// Look through all the options on the drop-down. If any are longer than the passed width value then expand the box.
// Params: 
// ddl - the dropdown list object
// maxWidthFlag - if true then set the max-width, else set the width
// width - the option width value over which the functionality is invoked.
function SetToAutoWidth(ddl, maxWidthFlag, width) {
    if ($.browser.msie && $.browser.version.substring(0, 2) === "8.") { // IE8 only

        var ID = $(ddl).attr("id");
        var ff = $(ddl).css("font-family");
        var fs = $(ddl).css("font-size");

        $("#" + ID + " > option").each(function () { // For each option in the list...

            var t = $(this).text(); // Get the text
            var w = FindTextWidth(t, ff, fs); // Find the width in pixels

            if (w >= width) { // If the width is greater than the passed value
                if (maxWidthFlag === true) {
                    $(this).parent().css("max-width", ""); // Remove the max-width style
                }
                else {
                    $(this).parent().css("width", "auto"); // Set the width style to auto
                }

            }
        });
    }
}

// Params: 
// ddl - the dropdown list object
// maxWidthFlag - if true then set the max-width, else set the width
// width - the option width value over which the functionality is invoked.
function ResetToFixedWidth(ddl, maxWidthFlag, width) {
    if ($.browser.msie && $.browser.version.substring(0, 2) === "8.") { // IE8 only

        if (maxWidthFlag === true){
             $(ddl).css("max-width", width + "px");
        }
        else{
            $(ddl).css("width", width + "px");
        }
    }
}

Note that this still does not get around the problem of the greater-than-the-max-width selected option overflowing the main drop-down box and overwriting the down-arrow. If anyone has a solution for that that doesn’t involve adding an image to it please say!

Unwanted whitespace in unordered lists using Internet Explorer

I had a drop-down menu which try as I might would not remove the whitespace from between the list item elements for IE. It turns out this is a bug with IE. The solution I eventually found was for IE only which was annoying. You simply need to set the the following style, either in your css sheet or directly:

.myIEclass
{
      ...
      margin-top: -3px;
}

Of course you may find that you need to set margin-left instead, depending on your whitespace.

I found that this issue only seemed to occur if I had more than one child element of my li tag, but whether that is the actual cause who knows? Could be anything with Internet Explorer!

If you don’t specifically target IE then the menu/list will appear squashed in the other browsers. As I was generating this menu via an MVC HtmlHelper I was simply able to target IE directly using the UserAgent property in C#:

HtmlTextWriter writer = new HtmlTextWriter(html.ViewContext.HttpContext.Response.Output);

...

if (HttpContext.Current.Request.UserAgent.Contains("MSIE") == true) //HACK: Lovely IE hack
        writer.AddAttribute(HtmlTextWriterAttribute.Style, "margin-top:-3px");

Of course if you are not generating via a helper you could also use HTML directives such as:

<!--[if IE 8]>
---
<![endif]-->

which is ugly, but a more robust way of doing it than CSS ‘hacks’, which I am not going to discuss here as it might give you bad ideas. 🙂

Word Wrap in CSS

The following does ‘proper’ word wrapping in all the browsers I tested it in (i.e. excluding IE6)

.wordwrap {
    white-space: pre-wrap; /* css-3 */
    white-space: -moz-pre-wrap; /* Mozilla, since 1999 */
    white-space: -pre-wrap; /* Opera 4-6 */
    white-space: -o-pre-wrap; /* Opera 7 */
    word-wrap: break-word; /* Internet Explorer 5.5+ */ 
}