matt laine dot com / blog

web development

[UPDATE] CSS text truncation with ellipsis…

by on Nov.09, 2010, under CSS, Firefox, web development, WebKit

Just an update…I’ve been getting a few comments on this and seems like the FireFox hack mentioned in the previous post will not work in FireFox4.

I did a quick check and it looks like they removed support for -moz-binding declarations in the CSS. This was probably for security reasons. See https://bugzilla.mozilla.org/show_bug.cgi?id=546857 and https://bugzilla.mozilla.org/show_bug.cgi?id=312156 for more information.

Until Firefox adds support for this, use with caution (or use WebKit).

1 Comment : more...

CSS text truncation with ellipsis…

by on Feb.12, 2010, under CSS, Firefox, web development, WebKit

I have been working on a project for several months now, that has a reoccurring feature for just about every line of text; the text truncates with an ellipsis (“…”). Why truncate your text when you can just let your text wrap naturally? There are several reasons why you might not want your text to wrap:

  • the size of the text (number of characters) is unknown
  • the text will get localized into several languages
  • the text might have a container with a fluid height or width
  • a page has to be compatible on mobile devices, multiple platforms

text-overflow: ellipsis;

The most obvious way to do this is to use the CSS rule text-overflow: ellipsis;. This will automatically clip your text depending on the size of the container, and apply “…”. A few important things to note are the white-space:nowrap and overflow:hidden must be in effect. Also, the element must either be a block-element (like DIV, P, or UL) or inline-block (if the browser supports it).

Here is an example:

<style type="text/css">

.truncate {
  white-space: nowrap;
  text-overflow: ellipsis;
  overflow: hidden;
}
</style>

<div class="container">
  <p class="truncate">Hi, I'm a really long line of text
that is going to get truncated by my 200 pixel wide parent element</p>
</div>

Hi, I’m a really long line of text that is going to get truncated by my 200 pixel wide parent element.

Text-overflow is supported on Internet Explorer and WebKit (Opera supports it’s own -o-text-overflow). If you are browsing in Firefox, you’ll notice that there is no ellipsis.

Works for IE6 but not Firefox?

Because, according to the Mozilla Developer Center text-overflow has no W3C specification, Firefox does not support this property. This has been a bug for a long time, and there is no indication on when Firefox support might become available.

Hacking Support in Firefox

What happens when browsers don’t behave the way we want? We hack.

The only working hack I’ve found is the same one discussed here and here, and basically using a XBL binding in CSS with the Firefox supported -moz-binding property. The CSS is bound to a XUL document and tells the element(s) to tail truncate, which by default applies an ellipsis.

XUL file (ellipsis.xml):

<?xml version="1.0"?>
<bindings 
  xmlns="http://www.mozilla.org/xbl"
  xmlns:xbl="http://www.mozilla.org/xbl"
  xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
>
	<binding id="ellipsis">
		<content>
			<xul:window>
				<xul:description crop="end" xbl:inherits="value=xbl:text"><children/></xul:description>
			</xul:window>
		</content>
	</binding>
</bindings>

And so in our CSS we’ll have:

<style type="text/css">
.truncate {
  white-space: nowrap;
  text-overflow: ellipsis;
  overflow: hidden;
  -moz-binding: url(ellipsis.xml#ellipsis);
}
</style>

Hi, I’m a really long line of text that is going to get truncated by my 200 pixel wide parent element.

Cross-browser Caveats

I found out the hard way that there are several caveats to using the Firefox XUL/XBL hack:

  • the ellipsis.xml file (and CSS file) must be on the same domain as the webpage! This means that if you are going to source to and external server for these assets, Firefox will not display properly
  • your text may no longer be selectable.
  • line-height may be ignored

Also, the CSS must select the element that is truncating; the style will not cascade down into other elements if there are siblings that are block-elements (like div, p, or ul);

<style type="text/css">
.truncate {
  width: 100px;
  white-space: nowrap;
  text-overflow: ellipsis;
  overflow: hidden;
  -moz-binding: url(ellipsis.xml#ellipsis);
}
</style>

<div class="truncate">
  Blah, blah, blah. I will not truncate!!
  <p class="truncate">Yessir! I truncate looooooong time!!</p>
</div>
Blah, blah, blah. I will not truncate!!

Yessir! I truncate looooooong time!!

10 Comments more...

New Twitter Mashup, Celebirdies.com

by on Aug.17, 2009, under api, twitter, web development

Announcing a new venture that I call Celebirdies. Celebirdies are actors, musicians and personalities, otherwise know as celebrities, that use social media like Twitter and TwitPic. Celebirdies.com brings celebrity tweets and twitpics into one easy to navigate site.

Celebirdies was built and designed by yours truly. It has a weekly featured celeb, as well as profiles on 350+ celebs. The site has been in development for 3-4 months, but new features will be added on a ad hoc basis. Currently you can search or browse from A-Z to find your favorite celebs. There is also the ability to social-bookmark the site and/or post to Twitter and Facebook. Future development will include support for different types of media, celebrity categories, comments, and up/down voting.

I’m going to be posting a series of articles related to the development of Celebirdies from concept to launch. Please take a chance to check out my site, and follow @celebirdies for the latest. Let me know what you think!

Leave a Comment : more...

Using Python and Shell to Manipulate Large Data

by on Apr.03, 2009, under python, shell scripting, web development

I’ve been working on a project where we are dealing with a large amount of data. So large in fact that it will crash most programs that try and open it (try parsing a 60MB CSV file with Excel). So, I’m using Mac OS X Terminal to read and sort through data using UNIX commands.

Often times, as a web developer, you are asked to step outside of your normal routine and do some investigating. In my case, I was asked to audit a website with over 30,000 pages. Simply figuring out how many pages were on the site was a large task on it’s own.

Getting a “complete” set of URLs

I say “complete” in quotes because for some sites it’s just not feasible to find 100% of the pages. The good thing is that a site that is really large will have a lot of stuff that the CEO or whoever just doesn’t care about. Still I wanted to capture as much data as I can as my starting point.

First, I used Xenu link sleuth to crawl the site for links. This is not the best way to crawl a site, but it was a quick way to get my set of URLs. On reason why this method isn’t the best is that it fails to find orphaned pages (pages that are not linked to by other pages). However, I does record some interesting tidbits about each page, such as the content-type, folder level relative to the site root, and whether or not the link was broken (404) or forbidden (403).

Bash that data into submission

After exporting this data out, my file was quick large. Since the data was in tab-delimited format, I knew I could extract the URLs by parsing the string after the first tab character. I use the awk program to do this.

$ awk -F"\t" 'OFS="\t"{print $1 >> url_results.txt}' url.txt

Next I wanted to filter out any duplicates. I do this by using sort with -u and -f flags. This basically tells the sort to treat uppercase and lowercase strings equally, and save the unique lines in a seperate text file

$ sort -u -f url_results.txt > url_results_uniq.txt

Enter the Python

Now my URL list is more manageable and I can play around with it. One thing that I wanted to do is see what the HTML source was for each file. This way, I can check to see what CSS files are linking from it, etc. Enter Python; a scripting language that also acts like an interpretor. It’s fast for processing large files and forces you to handle errors like 404s or page timeouts. Using Regular Expressions (again) to match the CSS file reference, my Python script goes a little like this:

#/bin/usr/python
import re, urllib2, socket
socket.setdefaulttimeout(10) ##this sets the timeout manually for each page


def analyzeData(url, htmlcode): ##this function does the regex matching
    patterns = dict(firststyle= '/pattern1.css/', secondstyle='/pattern2.css')
    for template in patterns:
        regex = str(patterns[template])
        if(re.search(regex, htmlcode)):
        #open file w/ template name, print url to it
            fh = "fileHandle"+template
            fh = open(template+".txt", "a")
            fh.write(url+'\n')
            print url+", "+template


f = open('urls_results_uniq.txt', 'r') ##opens the list of unique urls
httperror = open('httperror.txt', 'w') ##open/create a file to store HTTP errors
timeouts = open('timeouts.txt', 'w') ##open/create a file to store time outs
index = 0;
for line in f.readlines():
    urlregex = re.match('(^http:\/\/.*\.html|^http:\/\/.*\.php)', line) ##checks for .html or .php in url path (excludes images, css, etc.)
    if(urlregex):
        url = urlregex.group(1)
        try:
            req = urllib2.Request(url)
            page = urllib2.urlopen(req)
            htmlcode = page.read(1500)
            page.close()
            analyzeData(url, htmlcode);
        except urllib2.HTTPError: ##handle exceptions
            httperror.write(url+'\n')
            print str(index)+' - http error for '+url
        except urllib2.URLError:
            timeouts.write(url+'\n')
            print str(index)+' - url error (timeout) for '+url
        index = index + 1
f.close()
httperror.close()
timeouts.close()

The first part of the script imports the proper libraries. For the example, I’m using the Regular Expression library (re), urllib2 to send an HTTP request and return the HTML source, and socket to set the time out for each page manually. The second part is the function that actually does the regex matching on the html source code. I found out that the function had to be defined before it was called, which is different that what I’m used to in PHP. Lastly, I’m opening, reading, and looping through my list of URLs and calling the ‘analyzeData’ for each URL. If the function finds a match in the HTML source, it writes that to a file named either ‘firststyle’ or ‘secondstyle’.

My first impressions on Python

This was the first time I used Python. I’d previously tried to perform the method above using PHP and cURL. The script was continually timing out or stalling, so I changed technology. I also looked to see if I could leverage a Python framework for the task I was doing, such as Django, but for what I needed, I ended up using straight-up Python. Python is extremely fast, and I like the combination of using Python in the Shell. I also found it useful to print out something for each URL that I’m checking, that way I can know that the script is still running, even if no matches were found or an error was found.

Probably the hardest thing to get used to in Python is indenting the code. You have to indent properly or Python will not be able to interpret your code. I imagine this is an attempt to get rid of curly-braces, a la C or PHP, and to it’s credit, does force you to write code consistently. The down-side to using Python is that it’s a pretty big learning curve. I’m the type of person who won’t dive into something like Python with a project, or reason (other than curiosity).

Leave a Comment : more...

Domlets – bookmarks gone wild

by on Jan.23, 2009, under web development

Javascript bookmarklets have been around for quiet some time. In fact, most of the posts on bookmarklets.com are from the Netscape days. That said, I’d like to introduce a new way of looking at javascript bookmarks that I’m calling “domlets”

What’s a Domlet?

Domlet is a blend of DOM (or the Document Object Model that comprises a webpage) and bookmarklets (or favlets if you an IE-user). Domlets are used to manipulate the DOM using javascript to frame in a new content window.

Here is an example domlet for digg.com.

Another for twitter.com.

Domlets have only been tested on FireFox on Mac and Windows, and Safari on Mac. I’m hoping to have a version worked out for IE in the next few weeks.

How it works

Now for some technical stuff: the realization that you can execute javacript via the location or address bar was what lead me down the path of developing domlets. Web developers are usually well versed in bookmarklet tools like Visual Element which allows you to inspect the DOM for javascript events. Other sites like ma.gnolia.com incorporate use a bookmarket UI for social bookmarking, in that, any given page you are on, you can click the bookmarklet to view community comments or conversations related to that page.

What these scripts have in common is that they use the initial javascript to attach a script to the document.body node. Once attached, the script calls the function to initialize the script and perform whatever DOM-magic ensues. Domlets has a similar initialization approach. I also found that I had better success with my bookmark code if I attached the script using a self-executing function like this:

javascript:(function(){…script sniffed and attached here…})();

Also, I decided to use an object literal ‘Domlet’ to encapsulate my function names, thus avoiding any namespace issues I might have with scripts already loaded in the DOM. Here is the basic skeleton of a domlet:

var Domlet = {
defaults,
init : function(){},
addFrame :function(){}
}
…more functions here for animation and closing the frame
Domlet.init()

Lastly, I debated over whether or not to utilize a Javascript library for the domlet, but ended up not using a library and wrote everything myself (hence all the cross-browser bugs!). Had I used a library, I would have used JQuery, since the ‘min’ library is very light and I could easily implement it to create the same effects. In fact, this project was mainly inspired by the JQuery page-slide plugin, which uses a similar method of “wrapping” the page content and “throwing” it to the side to reveal a new content window.

Mobile Content Works Best

In formatting a domlet, I’ve chosen to capitalize on the growing number of mobile micro sites that are optimized for less than 320 pixels wide. There are several reasons for doing this; one is that I can simply iframe in the address of the micro site, like m.digg.com and m.twitter.com for the examples above. Another reason is to enable users to browse content formatted for mobile phones using their regular browser. I call this behavior “micro-browsing”. Often times, I just want to peek at my gmail or update my facebook status, but without opening up a new tab or leaving the page (pretty lazy, huh?). With domlets, I can simply load these micro-sites in a new pane and close it when I’m done.

As the web goes mobile in the next coming months, developers and users should establish standards for thinks like view-portal dimensions and CSS media types. What will be interesting is if/when developers and users decided on a standard, will micro-browsing really takes off as a behavior? Regardless, I hope that in developing domlets, I can encourage the development of a standard, i.e. one that works on your mobile and that domlets can render in your normal browser. Already, I think there are some 50+ mobile browsers, so settling on a standard will become increasingly difficult with time.

3 Comments :, , , more...

Looking for something?

Use the form below to search the site:

Still not finding what you're looking for? Drop a comment on a post or contact us so we can take care of it!

Visit our friends!

A few highly recommended friends...

Archives

All entries, chronologically...