Welcome to Code Couch

Resizing the left-hand labels column in Gmail – an update to fix text selection

Posted by at 9:57am on February 13, 2012.

Update 2 (10 May 2012): I’ve added a new post that has all the various bug fixes to date as well as new code showing how to get the code working as a user script in Chrome as well as Greasemonkey.

Update 1 (18 Feb 2012): I’ve added some example code that shows the modifications needed to allow this to work in Greasemonkey. I’ve also added back the comments above the “full monty” code that I’d removed, which link to the Gmail Greasemonkey API docs.

Here’s an update to my Gmail label column resizing code that fixes the issue of not being able to select any page content.

The first version didn’t have this problem as I’d not put any restrictions in place. Unfortunately this caused content to become selected while dragging the grab bar, which looked quite ugly. The second version permanently disabled the ability to select content from the right-hand column… not exactly a desirable feature if you needed to copy something from an email!

This update gives the best of both worlds: content is selectable at all times, except when the grab bar is being dragged.

As with the last few posts, there are three versions: a bookmarklet with all unnecessary whitespace and punctuation removed, a slightly larger version with some formatting, and lastly a fully commented version for those who want to understand how it works.

From the many emails I’ve received, I realised that not everyone necessarily knows how to use a bookmarklet, and so I’ve included some instructions at the end of this post detailing how to run the code in various web browsers. If after reading them you’re still having trouble getting the code to work, let me know.

Thanks for all the feedback you’ve been sending. It’s great to know that so many people are finding this useful!

The code

Here’s the no-whitespace bookmarklet version. Unlike the previous posts, I’ve included the “javascript:” prefix to make it even easier to use.

javascript:gmonkey.load(2,function(o){var i=o.getNavPaneElement(),r,l,c=o.getActiveViewElement(),d=c.ownerDocument,b=d.body,g,t=0,x,w,n,Q='querySelector',W='width',U='parentNode',P='px',S='style',k=function(){r[W]=b[o]-l[o]+P};while(c.compareDocumentPosition(i)&2)c=c[U];while(i[U]!=b)i=i[U];l=(n=c.childNodes)[0],r=n[1][S],n=l[Q]('[role^=n]'),g=i.appendChild(d.createElement('div'));r[(U='cssText')+2]=n[Q]('[role]')[S][U];while(t+=n.offsetTop,n=n.offsetParent);g[S][U]=W+':4px;position:absolute;z-index:9;left:'+(l[o='offsetWidth']-5)+'px;top:'+t+'px;bottom:0;cursor:ew-resize;cursor:col-resize;background:url(data:image/gif;base64,R0lGODlhAwAEAIAAAL+/v////yH5BAAAAAAALAAAAAADAAQAAAIERGKnVwA7)';top[n='addEventListener']('resize',k,0);g[n]('mousedown',function(e){e.which==1&&(x=l[o]-e.pageX,r[U+3]=r[U],r[U]+=r[U+2])},0);d[n]('mousemove',function(e){x&&(w=e.pageX+x,l[S][W]=w+P,g[S].left=w-5+P,k())},0);d[n]('mouseup',function(e){e.which==1&&(x=0,r[U]=r[U+3],k())},0)})

This update has pushed the size up a bit more than the last update did (it’s now 961 bytes)… but all the browsers I’ve tested this in seemed to handle bookmarklets of this size without any problems.

Here’s the same code, with a small amount of whitespace:

gmonkey.load(2, function(o) {
	var i=o.getNavPaneElement(), r, l, c=o.getActiveViewElement(), d=c.ownerDocument, b=d.body, g, t=0, x, w, n, Q='querySelector', W='width', U='parentNode', P='px', S='style', k=function() { r[W]=b[o]-l[o]+P; };
 	while(c.compareDocumentPosition(i)&2) c=c[U]; while(i[U]!=b) i=i[U];
	l=(n=c.childNodes)[0], r=n[1][S], n=l[Q]('[role^=n]'), g=i.appendChild(d.createElement('div'));
 	while(t+=n.offsetTop, n=n.offsetParent);
 	g[S][U] = W + ':4px;position:absolute;z-index:9;left:' + (l[o='offsetWidth']-5) + 'px;top:' + t + 'px;bottom:0;cursor:ew-resize;cursor:col-resize;background:url(data:image/gif;base64,R0lGODlhAwAEAIAAAL+/v////yH5BAAAAAAALAAAAAADAAQAAAIERGKnVwA7)';
 	top[n='addEventListener']('resize', k, 0);
	g[n]('mousedown', function(e) { e.which==1 && (x=l[o]-e.pageX, r[U+3]=r[U], r[U]+=r[U+2]); }, 0);
	d[n]('mousemove', function(e) { x && (w=e.pageX+x, l[S][W]=w+P, g[S].left=w-5+P, k()); }, 0);
	d[n]('mouseup', function(e) { e.which==1 && (x=0, r[U]=r[U+3], k()); }, 0);

And the full monty:

// Use the Gmail Greasemonkey API to reliably find the 2 main elements (left- and right-hand columns). You do not need Greasemonkey installed to use the API.
// See http://code.google.com/p/gmail-greasemonkey/wiki/GmailGreasemonkey10API for more details (note: documentation is out of date)
// Load the API. It will run our callback function when loaded, passing a GmailAPI object
gmonkey.load(2, function(o) {
	Set up variables. To keep the code size down, some are re-used. There is no global namespace pollution at all.
	i		this will end up pointing to the element that the grab bar is inserted into
	r		this points to the style object of the element that is resized in the right-hand column
	l		this will end up pointing to the element that is resized to change the left-hand column width
	c		this will end up pointing to the container that holds the left- and right- hand column elements l & r
	d		this points to the document in the frame we use
	b		this points to the body
	g		this points to the grab bar element
	t		this is used initially to calculate the top coordinate of the grab bar, but is re-used to point to the resize function
	x		this holds the initial x delta between mouse click and left-hand column width
	w		during dragging, this holds the new width of the left-hand column
	n		used for many things, most notably holding the string 'addEventListener'
	k		this is the function that sets the width of the right-hand column
	Q		this holds the string 'querySelector'
	W		this holds the string 'width'
	U		used for many things, most notably holding the strings 'parentNode' and 'cssText'
	P		this holds the string 'px'
	S		this holds the string 'style'
	var i=o.getNavPaneElement(), r, l, c=o.getActiveViewElement(), d=c.ownerDocument, b=d.body, g, t=0, x, w, n, Q='querySelector', W='width', U='parentNode', P='px', S='style', k=function() { r[W]=b[o]-l[o]+P; };
	// Update c and i to point to their intended elements
	while(c.compareDocumentPosition(i)&2) c=c[U];
	while(i[U]!=b) i=i[U];
	// Update l, r, and n to point to their intended elements. Create the grab bar, and insert into DOM
	l=(n=c.childNodes)[0], r=n[1][S], n=l[Q]('[role^=n]'), g=i.appendChild(d.createElement('div'));
	// Copy the inline style from the Compose button. At present, the only inline style is a browser-specific user-select style used to prevent text selection.
	// This is applied to the right-hand column while dragging is underway to stop the text being selected. It is removed afterwards so that text can be copied.
	// As I wanted to keep the code size as small as possible, it was easier to let Google do the browser sniffing and copy their style rather than deal with the vendor prefix approach.
	// Store this in a property called 'cssText2' of the right-hand column's style object
	// Calculate the top position for the grab bar
	while(t+=n.offsetTop, n=n.offsetParent);
	// Style the grab bar
	g[S][U] = W + ':4px;position:absolute;z-index:9;left:' + (l[o='offsetWidth']-5) + 'px;top:' + t + 'px;bottom:0;cursor:ew-resize;cursor:col-resize;background:url(data:image/gif;base64,R0lGODlhAwAEAIAAAL+/v////yH5BAAAAAAALAAAAAADAAQAAAIERGKnVwA7)';
	// Add a resize event to the window to update the size of the right-hand column as the window is sized
	// While Gmail does this anyway, it doesn't take into account any size change to the left-hand column
	top[n='addEventListener']('resize', k, 0);
	// Add event listeners. mousedown is added to the grab bar, mousemove and mouseup to the document
	g[n]('mousedown', function(e) {
		// Only trigger with LMB on grab bar. Stores initial x coord and width of left-hand column
		// Save a copy of the existing right-hand column style so we can restore it later, then disable text selection
		e.which==1 && (x=l[o]-e.pageX, r[U+3]=r[U], r[U]+=r[U+2]);
	}, 0);
	d[n]('mousemove', function(e) {
		// If we have the mouse button pressed, work out the x delta then update the column widths and the grab bar position
		x && (w=e.pageX+x, l[S][W]=w+P, g[S].left=w-5+P, k());
	}, 0);
	d[n]('mouseup', function(e) {
		// If LMB is released, stop the mousemove code from running and restore the right-hand column style
		// k() needs to be called again, as restoring the style alters the width
		e.which==1 && (x=0, r[U]=r[U+3], k());
	}, 0);

How to run the code

I’ll start by showing how to run the bookmarklet version of the code, followed by a quick overview of the one modification needed to run the code using Greasemonkey.

Running the code as a bookmarklet

I know that many people will be unfamiliar with the term "bookmarklet", or what they do. In a nutshell, they’re small pieces of code that are usually designed to add extra functionality to a web page, and they are normally run (activated) by clicking a link on a page or in your browser’s bookmarks / favourites list. If you want to find out more, take some time to read the Wikipedia article that covers the topic in great detail.

Note: these instructions are generic, as different web browsers have different interfaces and trying to detail every combination would make for a very long (and boring) read. If you have trouble getting the code to run, post a comment with the details of which web browser you’re using (including the version, if possible).

Regarding terminology, I’ll be referring to “bookmarks” throughout these instructions. If you’re more familiar with the term “favourites”, don’t worry – they’re exactly the same thing, so mentally substitute one word with the other :-)

  1. Select / highlight the no-whitespace / bookmarklet version of the code
  2. Copy it to your clipboard. If you’re running Windows, pressing Ctrl+C should copy the selected text, while Mac users should find Cmd+C (⌘+C) works. You’ll probably also find that right-clicking the code box will give you a menu with an option to copy. If you’re running Linux, you already know how to copy to the clipboard, and if not, there’s probably a twenty page “man” document you can read to find out :-)
  3. Now that you’ve got the code copied to your clipboard, you need to create a new bookmark to paste it into. How you do this depends on your web browser, but normally there will be a “Bookmarks” menu with an “Add” or “Manage” option
  4. You can set the name of the bookmark to be anything you like, but normally the name should be descriptive so you know what it is
  5. Set the URL / address of the bookmark to be the code that you copied earlier
  6. Save the bookmark

That’s half the job done… now to run the code:

  1. Load Gmail
  2. Wait for the page to finish loading
  3. Run the bookmark as you would any other bookmark (normally by selecting it from the “Bookmarks” menu / toolbar)
  4. If all has gone to plan, you should see two thin dotted lines between the label column and the main content area. They’re grey in colour, so if you’ve chosen a grey theme for Gmail, you might not see them. They look something like this (without the red border):

If you do not see two dotted lines in the red box above, then chances are the code will not work for you in Gmail. You might want to refer to the trouble-shooting steps below.

If you see the dotted lines in Gmail, then the code has loaded. You should be able to click and drag to change the width of the label column. If you do not see the lines in Gmail, or they are visible but are not draggable, here are some trouble-shooting steps you can try:

  • Ensure you’re running a modern web browser; this code will not work on ancient browsers. If you’re running Internet Explorer, you need to be using at least version 9. Up-to-date versions of Firefox, Safari, Chrome, Opera, or any other modern browser should run the code without any problems, as it’s all fairly run-of-the-mill code and doesn’t use any non-standard or browser-specific routines (I tested the previous version of the code in Firefox v3.6 and it worked perfectly)
  • Disable any Google Labs features you are using. Based upon the feedback left in the comments, it appears some of the Labs are not compatible with this code, or they change the layout of the page too much for the code to handle (for example, the Labs code that moves the chat section to the right-hand side of the page)
  • While I’ve tested this code on several Gmail themes on the classic and new look, it’s possible that some themes may not be compatible. If you’re using anything other than one of the default themes, try switching back and see if things start working
  • If you still cannot get the code to work, let me know. The more browsers I can get this to work in, the better. One exception is Internet Explorer: I’ve no intention of getting this to work with IE 6, 7, or 8. I still have flashbacks to the good old days when I had to debug IE 4 and Netscape 4 problems :-)

Running the code from Greasemonkey

To get the code to work in Greasemonkey, the “gmonkey” variable must be prefixed with “unsafeWindow.”. Here’s the wrapper I used around the above “full monty” code that I’ve tested working in Fx 10.0.2:

// ==UserScript==
// @name           Gmail label column resizer
// @namespace      http://www.codecouch.com/
// @description    User script that allows the labels column in Gmail to be resized
// @include        http://mail.google.com/*
// @include        https://mail.google.com/*
// ==/UserScript==
// Insert the code here, but don't forget to prefix "gmonkey" with "unsafeWindow."
// The first line of code should now read:
// 	unsafeWindow.gmonkey.load(2, function(o) {

Update: I’ve changed this code since first publishing it. I found that waiting for the window’s onload event would occasionally cause 2 grab bars to show up. If you’ve copied the old version that contains the following two lines, you should remove them:

window.addEventListener('load', function() {
}, false);

Feel free to tinker and modify the code as you see fit, but please follow the terms of use and acknowledge where the code came from. I don’t expect you to fit the license into the bookmarklet, obviously :-). If you do come up with any improvements, I’d love to see them!

Known issues

Here’s a list of the issues I’m currently aware of:

  • The grab bar disappears if the page is reloaded. If you find that it has vanished, try running the bookmarklet again. Alternatively, use a plugin like Greasemonkey to automatically run the code once the page has loaded
  • When switching between pages / themes, the grab bar may end up in the wrong place. If you experience this, reload the page and run the bookmarklet again
  • No minimum or maximum column size is enforced. If you drag a column too small, don’t expect things to look good :-)
  • The position of the grab bar isn’t persisted over a page reload. This is something I’m looking at adding to the next version
  • Certain Gmail labs options or plugins are not compatible with this code. Maintaining a complete list is impossible, but almost certainly, anything that adds a right-hand column (e.g. the Gmail “Right-side chat” lab, Xobni “Smartr Inbox” plugin, etc.) will cause problems

Thanks again for all your feedback. If you’ve got any suggestions on how I can improve this, please let me know.

Post to Twitter


There are 30 responses to this post.

  1. Your Gmail left column resizing code is great for people with extensive nested labels. Thx!
  2. I just discovered your site and was ecstatic to see a solution to this annoying pet peeve. Unfortunately, I cannot seem to get this to work. I am using the latest version of Firefox (10.0.2). I have tested it on several of my gmail accounts, some have labs features enabled but I have also tried it on one that has no labs features enabled and no themes installed either and it doesn't work on any of them. I am using Gmail Manager to handle all my accounts but I also tried disabling that and that didn't seem to help either. I am using the "full monty" version which I installed through Greasemonkey. Greasemonkey is showing that it is enabled and I've restarted my browser several times as well. I would love to get this working as I've been looking for a way to do this for a long time.
  3. Hi Ray, I don't use Greasemonkey myself, so it took some tracking down. The answer was in the Gmail Greasemonkey API docs which were linked to from my original post: "gmonkey" needs to be prefixed with "unsafeWindow.". I've edited this post and added a section showing how to run this code from Greasemonkey. Let me know if this doesn't work for you.
  4. Fantastic! Thank you so much for the quick response. It is working great now. I have discovered another "issue" though. I also use the xobni smarter inbox plugin (http://www.xobni.com/) which adds a small window to the right side of the window. Now, whether I resize the label column or not as soon as I open a message and click in the message pane itself the middle pane refreshes and the scroll bar and a portion of the pane disappears behind the xobni window. It's not a big issue and the scroll wheel still works to scroll through the message. I can definitely live with this issue now that I can resize the label window. Thanks again.
  5. Hi Ray, I've added a new post with updated code that attempts to play nicely with any extra columns that are present. Would you be able to test it with the Xobni plugin and let me know if it works or not? Thanks!
  6. Thanks a bunch for making this. I just recently moved to Gmail from Hotmail, and love everything about it, with my only complaint so far being this left column sizing annoyance. Surely Google will get around to fixing such an obvious interface issue... :-/ I use Safari, so can not use Greasemonkey. Do you know how I might have the bookmarklet/script automatically run when the Gmail page loads, rather than having to manually click it each time? I tried the script with a Greasemonkey-like Safari extension called NinjaKit, but a) I seemed to have to modify a bunch of the code as NinjaKit's JSLint syntax checker didn't like some of the formatting (which seemed odd to me, as it all looked like valid code, and does work), and b) the resizing bars never showed up when I would load Gmail, so I guess something about the NinjaKit script setup just didn't work (I suppose it's possible my code changes broke it, but I'm pretty sure my changes were simply benign reformatting).
  7. Hm, well I ended up making a Safari extension that injects the script upon page loading, and I spent a long time trying to debug it, but in the end it simply refuses to recognize the gmonkey and unsafeWindow vars. What makes it more frustrating is that the debugger shows that the gmonkey var exists and is a real object, EXCEPT within the scope of my (copy of your) script. It literally suddenly becomes invisible within my script, then is back in scope or whatever once my script returns. After turning on the script debugger for NinjaKit, turns out it was failing for the exact same reason (unidentified "unsafeWindow" and "gmonkey"). This is the first time I've ever really tinkered with any web dev like this, so I really don't know exactly what Safari is doing or the reason for this. It's almost as though it is hiding any custom global vars from my script, but that would go against what Apple's reference documentation says about injected scripts having all the rights and access as a host-side script. What's also strange is that the bookmarklet approach works fine for me (but I'd like the Greasemonkey-style approach so the resizing bar just automatically shows up each time I go to Gmail). So for some reason clicking the bookmarklet to activate the script works while having it automatically run upon page load does not seem to work. Any ideas?
  8. Great fix from the last one. I have a "minor" bug. When you have people in your "Chat" and you hover over their name, a popup is shown about the person. The problem is that the resize line is showing "over" the popup instead of behind it.
  9. Good Day Dan Just wanted to say thanks for your excellent fix for the resizeable Gmail labels column. I organise my mail obsessively, and I've been looking for something like this for months. Love your work Alex Bramford Monterrey, Mexico
  10. Hi Jason, Thanks for letting me know about this. I'll see if I can come up with a fix, and will post a comment with any details.
  11. Great implementation. Yes, it would be nice if this worked on Greasemonkey. On Safari through NinjaKit it does not seem to work. One observation: in GM, when you rollover the name list in the lower left corner to see details about the particular name that box seems to end up UNDER the strip that resizes the column. Little stack tweaking there. Thanks for making this work
  12. @Jason: If you change the code from reading "z-index:9" to "z-index:1", the bar should go under the people popups (at least, it does for me in Chrome). If that doesn't work for you, use the code from my latest post (with multi-column support), and edit the z-index on that from 9 to 1. If it still doesn't work, can you let me know what browser you're using, and also the version?
  13. Ive already noted your commments that it doesnt work with all Labs. I use 3 labs and here is what i noticed. Green Robot - No Problem it still worked. Preview Pane - I like to have a split pane with my emails at the top and the message to read below. It worked Ride Side Chat - I like to see my Gtalk friends on the right side of my page. THIS ONE STOPPED IT FROM WORKING. Not sure if there is a fix or you can even be bothered trying, but this was the one down point for me. Other than that, great to be able to see the left menu properly.
  14. Hi Steven, Since this code was written, I've added a new post (dated Feb 19th) that fixes the code not working with right-side chat. Shout if you find any problems with it.
  15. Hi, Ive been trying to use this code which i understand is the latest code and Im using google chrome. If the chat is enabled on the right it shows the two grey lines but when i try and move it the grey lines move but the size of the column on the left doesnt expand. It just pushed the grey lines over the email message.
  16. Hi Steven, The latest code is not the code in this post... it is the code in the post dated Feb 19th (see the 'Latest posts' section on the home homepage).
  17. Works great thanks. God only knows why google dont have that in their labs. I use a lot of folders and subfolders in that left menu and it was a real pain to navigate before.
  18. That's great news. Shout if you find any issues with it!
  19. OMG - I can't tell how happy I am to have found this! TOTALLY means I can now use gmail instead of having to use the slow Outlook - my nested folders were unusable before - totally love this! Philippe
  20. Thanks so much for this and for adding the instructions for the bookmarklet. I have a small second monitor at work and the left gmail label column was too large. I can now resize. Not sure why google mad it this large by default . . .
  21. I've added a new post dated 10 May 2012 that shows how to get this working as a user script in Chrome as well as Greasemonkey. See the update at the top of this post for details.
  22. Awesome... Finally makes Gmail tolerable. Thank you very much.
  23. That's brilliant! Thanks so much for this, Dan. I've been wanting this feature for a long time but I've never looked beyond Google itself for a solution. You're a whizz! DC
  24. PS: I should have said - I use Firefox with a lot of plugins, and I use at least half of the labs features in Gmail. I've got over three screen-heights' worth of labels, so nesting has always seemed like an attractive idea if only we could see the darned things! This could be the solution as long as I don't uncover any hidden bugs as time goes by. Thanks again.
  25. Great work. Thanks
  26. Thanks for this wonderful script. You have made many of my end users quite happy with it. Forgive that I have not taken the time to read all of the posts above which may actually have the answer I am seeking, but it seems there are 3 issues with this script. 1. One activated and the bar is dragged, the settings button disappears. 2. (Less severe) When dragging the bar there seem to be some "dead" spots where if you let it go the entire righthand pane is blank? Any way to resolve that? 3. Is there any way to make it stick without having to click the bookmark each time the browser is reloaded?
  27. @Greig: Regarding point 2, do you have any labs or gmail plugins enabled? If so, try disabling them and see if that helps.
  28. @Greig: Regarding point 3, if you use Firefox, you could use the GreaseMonkey extension. If you use Chrome, you could use my Chrome UserScript wrapper (documented in my latest post).
  29. All: For anyone finding that this code no longer works since late March 2013, please read my latest post dated 31st March 2013. It offers an updated wrapper that works with the latest Gmail changes.
  30. Thanks for the great share! Now I can see my complete folder names. Thanks for putting this together.

Leave a reply

You must either log in or enter your name and email address to post a comment.

Your email address will not be published.

  • You do not need to log in to comment, but you can if you wish.
  • Log in