Title to URL – WordPress style

Have you ever wanted to immediatly create a valid URL from a text input field or some kind of semantic input filed, I know I’ve had to implement it a few times now for different projects. Typically it will be something like, this new created object needs a name or title and that name or title needs to be converted into a URL which the user can see, and then edit to something more meaningful should they choose to. A good example of this already in action is in your wordpress blog, when you type in your post title Worpress automatically creates a URL for you and allows you to then edit and change the URL without also annoyingly changing the original title.

I’ve implemented this a few times now for different applications so here is the base code I tend to start with.

View Demo :: MooTools
View Demo :: jQuery

Notice how after editing the URL field the title will stop changing the URL value. The logic being that once you have edited the URL to your liking, updating the title will cease to overwrite your new URL.

The HTML


http://blah.com/ edit

Add this html to your page. Take notice of the ID’s that is how the script will target all the necessary elements.

The Style

.myinput {
	display:none;
}

Very basic stuff here. Theres more styles in the demo files.

The MooTools Javascript


var urlBuilder = new Class({

	Implements: [Options, Events],
	
	options: {
		title : {
			regx	: [/[^0-9a-zA-Z\s.,'!@%#)(:\;\"]+?/,/\s+/g,/^[\s]+/g,/([0-9a-zA-Z\s.,'!@%#)(:\;\"]{50})(.+)/i],
			rep		: ['',' ','', '$1']
		},
		url : {
			regx	: [/[^0-9a-zA-Z]+?/g,/[\-]+/g,/^[-]+/,/[-]+$/],
			rep		: ['-','-','','-']
		}
	},
	
	initialize: function(element, urlFlag, options){
		this.setOptions(options);
		this.inputObj 	= element;
		this.newUrl		= $(element.id+'_url');
		this.updateUrl	= $(element.id+'_update');
		this.edit		= $(element.id+'_edit');
		this.ChangedURL = (!urlFlag ? false : urlFlag);
		this.init();
	},
	
	init: function() {
		this.inputObj.addEvent('keydown', this.getValue.bindWithEvent(this,  this.options.title ));
		this.inputObj.addEvent('keyup', this.getValue.bindWithEvent(this,  this.options.title ));
		this.newUrl.addEvent('keyup', this.getValue.bindWithEvent(this, this.options.url));
		this.edit.addEvent('mousedown', this.showURL.bindWithEvent(this));
	},
	
	
	getUri: function(uri, o) {
		o.regx.each(function(index, key) {		
			uri = uri.replace(index, o.rep[key]);
		});
		return uri;
	},
	
	getValue: function(e, o) {
		var input = getTarget(e);
		if (input == this.newUrl) this.ChangedURL=true;
		
		var tmp = this.getUri(input.value, o);
		
		if (input.value != tmp )
			input.value = tmp;
		
		if (input != this.inputObj || !this.ChangedURL) {
			tmp = this.getUri(tmp, this.options.url);
			this.newUrl.value = tmp.toLowerCase();
			this.update(tmp + (tmp != '' ? '/' : ''));
		}
	},
	
	showURL: function(e) {
		var o = this.newUrl;
		var u = this.updateUrl;
		if (o.getStyle('display') == 'none') { 
			o.setStyle('display', 'inline-block');
			u.setStyle('display', 'none');
			this.edit.set('html', 'Save');
		} else { 
			o.setStyle('display', 'none');
			u.setStyle('display', '');
			this.edit.set('html', 'edit');
		}
	},
	
	update: function(s) {
		this.updateUrl.set('html', s);
	}
	
});

The jQuery Javascript

function urlBuilder(el, urlFlag, options) {
	
	// Becomes this.options
	var defaults = {
		title : {
			regx	: [/[^0-9a-zA-Z\s.,'!@%#)(:\;\"]+?/,/\s+/g,/^[\s]+/g,/([0-9a-zA-Z\s.,'!@%#)(:\;\"]{30})(.+)/i],
			rep		: ['',' ','', '$1']
		},
		url : {
			regx	: [/[^0-9a-zA-Z]+?/g,/[\-]+/g,/^[-]+/,/[-]+$/],
			rep		: ['-','-','','-']
		}
	}
	
	this.options 	= jQuery.extend(defaults, options);
	
	this.init = function(el, urlFlag) {
		var id 			= el;
		this.inputObj 	= $('#'+el); 	
		this.newUrl		= $('#'+id+'_url');
		this.updateUrl	= $('#'+id+'_update');
		this.edit		= $('#'+id+'_edit');
		this.ChangedURL = (!urlFlag ? false : urlFlag);
		this.events();
    };

	this.events = function() {	
		var obj = this;
		this.inputObj.bind('keyup', function(e) {					 
		  	obj.loadUri(e, 'title');
		});
		this.newUrl.bind('keyup', function(e) {		 
		  	obj.loadUri(e, 'url');
		});
		this.edit.bind('mousedown', function(e) {
			e.preventDefault();						 
		  	obj.showURL(e.target);
		});
	};
	
	this.loadUri = function(e, str) {
		e.preventDefault();					
		this.getValue(e.target, this.options[str]);
	};
	
	this.getUri = function(uri, o) {
		var collection = o.regx;
		for (index in collection) {
			uri = uri.replace(collection[index], o.rep[index]);
		}	
		return uri;
	};
	
	this.getValue = function(e, o) {
		var input = $(e);
		if (input[0] == this.newUrl[0]) this.ChangedURL=true;
		var tmp = this.getUri(input.attr('value'), o);
		input.attr('value', tmp);
		if (input[0] != this.inputObj[0] || !this.ChangedURL) {
			tmp = this.getUri(tmp, this.options.url);
			this.newUrl.attr('value', tmp.toLowerCase());
			this.update(tmp + (tmp != '' ? '/' : ''));
		}
	};
	
	this.showURL = function(e) {
		var o = this.newUrl;
		var u = this.updateUrl;
		if (o.css('display') == 'none') { 
			o.css('display', 'inline-block');
			u.css('display', 'none');
			this.edit.html('save');
		} else { 
			o.css('display', 'none');
			u.css('display', '');
			this.edit.html('edit');
		}
	};
	
	this.update = function(s) {
		this.updateUrl.html(s);
	};
	
    this.init(el, urlFlag);
	
}

Usage

// MooTools
window.addEvent('domready', function() {
	new urlBuilder($('pTitle'));
});
// jQuery
$(document).ready(function() {
	new urlBuilder('pTitle');						   	
});

The only thing to be aware of here is that I’m also giving you a field called ‘urlFlag’ set it to true in your call to urlBuilder like this if the user has previously edited the URL field.

        new urlBuilder($('pTitle'), true, { //options });

If not set it will default to false and assume the title will still update the URL field.

Options

	title : {
		regx : [/[^0-9a-zA-Z\s.,'!@%#)(:\;\"]+?/,/\s+/g,/^[\s]+/g,/([0-9a-zA-Z\s.,'!@%#)(:\;\"]{50})(.+)/i],
		rep : ['',' ','', '$1']
	},
	url : {
		regx : [/[^0-9a-zA-Z]+?/g,/[\-]+/g,/^[-]+/,/[-]+$/],
		rep : ['-','-','','-']
	}

Here is the best part of this little plugin. In the options you have two objects, ‘title’ and ‘url’ consisting of four simple regular expressions. You can overide these from your set-up calls, or you can leave them as is. Basically I’m just giving you control over what to filter out on the input and url, and what to leave in.

The ‘title’ option simply has four regex values in the array ‘regx’, and it has four matching replacment values in the array ‘rep’, so when the pattern from regx[0] is matched it will be replaced by the value of rep[0].

The ‘url’ option works in the exact same way. I’ve done it this way to provide simpler regex values for those that are not rocket scientists. Plus I highly doubt its possible to build a regex that accomplishes everything we need to do to strip a title down for publishing whilst turning it into a valid URL for location.

Required

  • MooTools Core
  • jQuery Core

Download

Contains both MooTools and jQuery versions

URL Builder

4 Comments

  1. Simon says:

    Ooops, quick update to the jQuery javaScript, was shorting a value to obj.value = ‘blah’, when in hindsight it of course should be obj.attr(‘value’, ‘blah’);

    Enjoy!

  2. Mark says:

    Hey Simon,

    Great post and great jquery function! One thing, how can we make the url lower case – so if the user types Test Article, the url would test-article.

  3. Simon says:

    Change the line “var tmp = this.getUri(input.attr(‘value’), o);”

    to the following

    “var tmp = this.getUri(input.attr(‘value’), o).toLowerCase();”

  4. nice post bro. waiting for the next part to show up