pr0g33k

 collapse all
  1. Clearing Containers with overflow: hidden

    I recently completed a side-project where I needed to create two columns of text floated left and right. The text of each column was laid out in an unordered list and was dynamically generated using data from a database. This meant that one column could, at any point, be taller than the other column. This was fine until I wanted to place a vertical line separating the two columns. I could have used a border on the left or right of either unordered list but if one grew taller, the border of the other wouldn't grow with it. Then it hit me, why not use a background image in the container? Here's the markup and corresponding CSS:

    <div class="columns">
        <div class="column1">
            <ul>
                <li>Column 1, Item 1</li>
                <li>Column 1, Item 2</li>
                <li>Column 1, Item 3</li>
                <li>Column 1, Item 4</li>
            </ul>
        </div>
        <div class="column2">
            <ul>
                <li>Column 2, Item 1</li>
                <li>Column 2, Item 2</li>
                <li>Column 2, Item 3</li>
                <li>Column 2, Item 4</li>
                <li>Column 2, Item 5</li>
                <li>Column 2, Item 6</li>
                <li>Column 2, Item 7</li>
                <li>Column 2, Item 8</li>
            </ul>
        </div>
    </div>
        
    <style>
        .columns {
            background: url(/images/vertical-line.png) repeat-y 50% 0;
            width: 402px;
        }
    
        .column1 {
            float: left;
            width: 200px;
        }
    
        .column2 {
            float: right;
            width: 200px;
        }
    </style>
        

    Unfortunately, this is what I got:

    • Column 1, Item 1
    • Column 1, Item 2
    • Column 1, Item 3
    • Column 1, Item 4
    • Column 2, Item 1
    • Column 2, Item 2
    • Column 2, Item 3
    • Column 2, Item 4
    • Column 2, Item 5
    • Column 2, Item 6
    • Column 2, Item 7
    • Column 2, Item 8

    Where's my background image? It turns out that a container of floated elements does not automatically clear the floats of its child elements. The container instead assumes its default height (0px in the case of  a div) or whatever height it is assigned. I don't want to assign a height in this case, though; I want the container to size to the height of whichever child is tallest.

    The solution is to set the overflow to hidden on the container. If I change the CSS to this:

        <style>
            .columns {
                background: url(/images/vertical-line.png) repeat-y 50% 0;
                overflow: hidden;
                width: 402px;
            }
    
            .column1 {
                float: left;
                width: 200px;
            }
    
            .column2 {
                float: right;
                width: 200px;
            }
        </style>
        

    Then this is what I get:

    • Column 1, Item 1
    • Column 1, Item 2
    • Column 1, Item 3
    • Column 1, Item 4
    • Column 2, Item 1
    • Column 2, Item 2
    • Column 2, Item 3
    • Column 2, Item 4
    • Column 2, Item 5
    • Column 2, Item 6
    • Column 2, Item 7
    • Column 2, Item 8

    It also means that I don't have to follow the container with an element assigned "clear: both" to clear the preceding floats.

    Posted on 7/23/2013 at 02:07 PM , Edited on 7/23/2013 at 02:07 PM
    Tags: HTML5CSS
  2. Managing Files Using Microsoft Azure Blob Storage (Part 2)

    In part 1, we set up an MVC project to connect to Microsoft's Azure Blob Storage and also created a view to upload some files. Now let's add a view to list the files we've uploaded.

    To start, add an ActionResult function to the FileManagerController named "List" and create a view for it (List.cshtml).

    Now, add some ActionLink's to the Index.cshtml view to navigate to our List view.

    @{
        ViewBag.Title = "File Manager";
    }
    <p>@Html.ActionLink("View Images", "List", new { id = "Images" })</p>
    <p>@Html.ActionLink("View Files", "List", new { id = "Files" })</p>
        

    The ActionLinks navigate to our List view - one for images and one for files. The URL's are: "/FileManager/List/Images" and "/FileManager/List/Files"

    To pass the name of the container and the list of URL's from Azure Blob Storage, I created the following class and placed it in my Models folder:

    public class ResourcesModel
    {
        public String Container { get; set; }
        public List<String> Urls { get; set; }
    }
        

    Change the List controller action to look like this:

    public ActionResult List(String id)
    {
        ResourcesModel resourcesModel = new ResourcesModel();
    
        if (!String.IsNullOrEmpty(id))
        {
            CloudBlobContainer cloudBlobContainer = GetCloudBlobContainer(id);
            BlobResultSegment blobResultSegment = cloudBlobContainer.ListBlobsSegmented(null);
    
            resourcesModel.Container = id;
            resourcesModel.Urls = blobResultSegment.Results.Select(r => r.Uri.ToString()).ToList();
    
            while (blobResultSegment.ContinuationToken != null)
            {
                blobResultSegment = cloudBlobContainer.ListBlobsSegmented(blobResultSegment.ContinuationToken);
                resourcesModel.Urls.AddRange(blobResultSegment.Results.Select(r => r.Uri.ToString()));
            }
        }
    
        return View(resourcesModel);
    }
    

    The GetCloudBlobContainer function is listed in part 1.

    I use CloudBlobContainer.ListBlobsSegmented() here because the number of blobs you can return from a single call is limited to 5000. If there are more than 5000 blobs, a continuation token is sent back with the request. The continuation token is basically a marker that can be used by subsequent calls to pick up where the last call left off. First, I call ListBlobsSegmented and pass null for the continuation token to get as many items up to the limit. Then, I loop for as long as a continuation token is returned and get those results, too. I'm only interested in the URI of each blob so I use the Linq Select() extension method to query out the URI's and add them to my Urls (List<String>) collection.

    Here's the List.cshtml view:

    @model RobertGaut.Pr0g33k.Web.Areas.Admin.Models.ResourcesModel
    @using RobertGaut.Core.Extensions
    @{
        ViewBag.Title = "Resources";
    }
    <p>@Html.ActionLink(String.Format("Upload to {0}", Model.Container.ToTitleCase()), "Upload", new { id = Model.Container.ToTitleCase() })</p>
    <ul id="resources">
        @foreach (String url in Model.Urls)
        {
            <li>@Html.ActionLink(Path.GetFileName(url), "Edit", new { id = HttpServerUtility.UrlTokenEncode(System.Text.UTF8Encoding.ASCII.GetBytes(url)), container = Model.Container }, new { data_href = url })</li>
        }
    </ul>
    <div id="preview"></div>
    @section scripts{
        <script>
            $(document).ready(function () {
                $("#resources > li").each(function () {
                    var lip = $(this).position();
                    var liw = $(this).width();
                    var a = $(this).children('a').first();
                    var href = a.attr("data-href");
                    var ext = href.substr(href.lastIndexOf('.') + 1);
                    $(this).css({ "background": "url(/images/" + ext + ".png) no-repeat 50% 0" });
                    switch (ext) {
                        case "jpg":
                        case "png":
                        case "gif":
                            a.hover(function () {
                                $("#preview").html("<img id='preview-image' src='" + href + "' /><div></div>");
                                $("#preview-image").load(function () {
                                    $("#preview").show();
                                    var h = $(this).height();
                                    var w = $(this).width();
                                    var h2 = h > 240 ? 240 : h;
                                    var w2 = Math.floor((h2 / h) * w);
                                    $(this).css({ "height": h2 + "px", "width": w2 + "px" });
                                    $("#preview").css({ "position": "absolute", "top": lip.top - h2 + 105 + "px", "left": Math.floor((lip.left + (liw / 2)) - (w2 / 2)) + "px" });
                                    $("#preview > div").first().text(w + " X " + h);
                                });
                            }, function () {
                                $("#preview").html('');
                                $("#preview").hide();
                            });
                            break;
                    }
                });
            });
        </script>
    }
        

    There's a lot going on there, jQuery-wise, but basically I check the extension of each file and set a background image to the <li> that represents that file type. Then, if the file is an image, I create a hover effect to display a smaller preview. This keeps the browser from downloading every image when this page loads. Images are only downloaded when the user hovers over its anchor tag.

    I have a small amount of CSS that accompanies this view:

    ul#resources {
        margin: 0;
        padding: 0;
    }
    
        ul#resources li {
            display: inline-block;
            height: 142px;
            margin: 5px;
            position: relative;
            min-width: 310px;
        }
    
            ul#resources li a {
                border: 0;
                top: 128px;
                display: block;
                position: relative;
                text-align: center;
                width: 100%;
            }
    
    div#preview {
        background: #000;
        border: 2px solid #000;
        display: none;
        text-align: center;
    }
    
        div#preview > div {
            color: #fff;
            height: 20px;
        }
        

    In part 3 we'll add a view to edit the file.