I'm working on an app in CodeIgniter, and I am trying to make a field on a form dynamically generate the URL slug. What I'd like to do is remove the punctuation, convert it to lowercase, and replace the spaces with hyphens. So for example, Shane's Rib Shack would become shanes-rib-shack.
Here's what I have so far. The lowercase part was easy, but the replace doesn't seem to be working at all, and I have no idea to remove the punctuation:
$("#Restaurant_Name").keyup(function(){
var Text = $(this).val();
Text = Text.toLowerCase();
Text = Text.replace('/\s/g','-');
$("#Restaurant_Slug").val(Text);
});
This question is related to
javascript
jquery
regex
The accepted answer didn't meet my needs (it allows underscores, doesn't handle dashes at the beginning and end, etc.), and the other answers had other issues that didn't suit my use case, so here's the slugify function I came up with:
function slugify(string) {
return string.trim() // Remove surrounding whitespace.
.toLowerCase() // Lowercase.
.replace(/[^a-z0-9]+/g,'-') // Find everything that is not a lowercase letter or number, one or more times, globally, and replace it with a dash.
.replace(/^-+/, '') // Remove all dashes from the beginning of the string.
.replace(/-+$/, ''); // Remove all dashes from the end of the string.
}
This will turn ' foo!!!BAR_-_-_baz-' (note the space at the beginning) into foo-bar-baz
.
For people already using lodash
Most of these example are really good and cover a lot of cases. But if you 'know' that you only have English text, here's my version that's super easy to read :)
_.words(_.toLower(text)).join('-')
More powerful slug generation method on pure JavaScript. It's basically support transliteration for all Cyrillic characters and many Umlauts (German, Danish, France, Turkish, Ukrainian and etc.) but can be easily extended.
function makeSlug(str)_x000D_
{_x000D_
var from="? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? a a ä á à â å c c e e e é è ê æ g g ö ó ø ? ô o ? ? n ? r s ü ß r l d þ h ? i ï í î j k l n n n r š s t u ú û ? ù ü u u ý ÿ ž z z ç ? ?".split(' ');_x000D_
var to= "a b v g d e e zh z i y k l m n o p r s t u f h ts ch sh shch # y # e yu ya a a ae a a a a c c e e e e e e e g g oe o o o o o m n n p r s ue ss r l d th h h i i i i j k l n n n r s s t u u u u u u u u y y z z z c ye g".split(' ');_x000D_
_x000D_
str = str.toLowerCase();_x000D_
_x000D_
// remove simple HTML tags_x000D_
str = str.replace(/(<[a-z0-9\-]{1,15}[\s]*>)/gi, '');_x000D_
str = str.replace(/(<\/[a-z0-9\-]{1,15}[\s]*>)/gi, '');_x000D_
str = str.replace(/(<[a-z0-9\-]{1,15}[\s]*\/>)/gi, '');_x000D_
_x000D_
str = str.replace(/^\s+|\s+$/gm,''); // trim spaces_x000D_
_x000D_
for(i=0; i<from.length; ++i)_x000D_
str = str.split(from[i]).join(to[i]);_x000D_
_x000D_
// Replace different kind of spaces with dashes_x000D_
var spaces = [/( | | )/gi, /(—|–|‑)/gi,_x000D_
/[(_|=|\\|\,|\.|!)]+/gi, /\s/gi];_x000D_
_x000D_
for(i=0; i<from.length; ++i)_x000D_
str = str.replace(spaces[i], '-');_x000D_
str = str.replace(/-{2,}/g, "-");_x000D_
_x000D_
// remove special chars like &_x000D_
str = str.replace(/&[a-z]{2,7};/gi, '');_x000D_
str = str.replace(/&#[0-9]{1,6};/gi, '');_x000D_
str = str.replace(/&#x[0-9a-f]{1,6};/gi, '');_x000D_
_x000D_
str = str.replace(/[^a-z0-9\-]+/gmi, ""); // remove all other stuff_x000D_
str = str.replace(/^\-+|\-+$/gm,''); // trim edges_x000D_
_x000D_
return str;_x000D_
};_x000D_
_x000D_
_x000D_
document.getElementsByTagName('pre')[0].innerHTML = makeSlug(" <br/> ‪???&???<strong>??_????</strong>?…???????????\r???\n?–?????? ??????\t \n?\t??????´? ??\\?????–????????\t????.Danke schön!ich heiße=?áÞÿá-Skånske,København çagatay rí gé tor zöldülésetekrol - . ");
_x000D_
<div>_x000D_
<pre>Hello world!</pre>_x000D_
</div>
_x000D_
You may want to take a look at the speakingURL plugin and then you just could:
$("#Restaurant_Name").on("keyup", function () {
var slug = getSlug($("#Restaurant_Name").val());
$("#Restaurant_Slug").val(slug);
});
Note: if you don't care about an argument against the accepted answer and are just looking for an answer, then skip next section, you'll find my proposed answer at the end
the accepted answer has a few issues (in my opinion):
1) as for the first function snippet:
no regard for multiple consecutive whitespaces
input: is it a good slug
received: ---is---it---a---good---slug---
expected: is-it-a-good-slug
no regard for multiple consecutive dashes
input: -----is-----it-----a-----good-----slug-----
received: -----is-----it-----a-----good-----slug-----
expected: is-it-a-good-slug
please note that this implementation doesn't handle outer dashes (or whitespaces for that matter) whether they are multiple consecutive ones or singular characters which (as far as I understand slugs, and their usage) is not valid
2) as for the second function snippet:
it takes care of the multiple consecutive whitespaces by converting them to single -
but that's not enough as outer (at the start and end of the string) whitespaces are handled the same, so is it a good slug
would return -is-it-a-good-slug-
it also removes dashes altogether from the input which converts something like --is--it--a--good--slug--'
to isitagoodslug
, the snippet in the comment by @ryan-allen takes care of that, leaving the outer dashes issue unsolved though
now I know that there is no standard definition for slugs, and the accepted answer may get the job (that the user who posted the question was looking for) done, but this is the most popular SO question about slugs in JS, so those issues had to be pointed out, also (regarding getting the job done!) imagine typing this abomination of a URL (www.blog.com/posts/-----how-----to-----slugify-----a-----string-----
) or even just be redirected to it instead of something like (www.blog.com/posts/how-to-slugify-a-string
), I know this is an extreme case but hey that's what tests are for.
a better solution, in my opinion, would be as follows:
const slugify = str =>_x000D_
str_x000D_
.trim() // remove whitespaces at the start and end of string_x000D_
.toLowerCase() _x000D_
.replace(/^-+/g, "") // remove one or more dash at the start of the string_x000D_
.replace(/[^\w-]+/g, "-") // convert any on-alphanumeric character to a dash_x000D_
.replace(/-+/g, "-") // convert consecutive dashes to singuar one_x000D_
.replace(/-+$/g, ""); // remove one or more dash at the end of the string
_x000D_
now there is probably a RegExp ninja out there that can convert this into a one-liner expression, I'm not an expert in RegExp and I'm not saying that this is the best or most compact solution or the one with the best performance but hopefully it can get the job done.
//
// jQuery Slug Plugin by Perry Trinier ([email protected])
// MIT License: http://www.opensource.org/licenses/mit-license.php
jQuery.fn.slug = function(options) {
var settings = {
slug: 'slug', // Class used for slug destination input and span. The span is created on $(document).ready()
hide: true // Boolean - By default the slug input field is hidden, set to false to show the input field and hide the span.
};
if(options) {
jQuery.extend(settings, options);
}
$this = $(this);
$(document).ready( function() {
if (settings.hide) {
$('input.' + settings.slug).after("<span class="+settings.slug+"></span>");
$('input.' + settings.slug).hide();
}
});
makeSlug = function() {
var slug = jQuery.trim($this.val()) // Trimming recommended by Brooke Dukes - http://www.thewebsitetailor.com/2008/04/jquery-slug-plugin/comment-page-1/#comment-23
.replace(/\s+/g,'-').replace(/[^a-zA-Z0-9\-]/g,'').toLowerCase() // See http://www.djangosnippets.org/snippets/1488/
.replace(/\-{2,}/g,'-'); // If we end up with any 'multiple hyphens', replace with just one. Temporary bugfix for input 'this & that'=>'this--that'
$('input.' + settings.slug).val(slug);
$('span.' + settings.slug).text(slug);
}
$(this).keyup(makeSlug);
return $this;
};
This helped me with the same problem !
Combining a variety of elements from the answers here with normalize provides good coverage. Keep the order of operations to incrementally clean the url.
function clean_url(s) {
return s.toString().normalize('NFD').replace(/[\u0300-\u036f]/g, "") //remove diacritics
.toLowerCase()
.replace(/\s+/g, '-') //spaces to dashes
.replace(/&/g, '-and-') //ampersand to and
.replace(/[^\w\-]+/g, '') //remove non-words
.replace(/\-\-+/g, '-') //collapse multiple dashes
.replace(/^-+/, '') //trim starting dash
.replace(/-+$/, ''); //trim ending dash
}
normlize('NFD')
breaks accented characters into their components, which are basic letters plus diacritics (the accent part). replace(/[\u0300-\u036f]/g, "")
purges all the diacritics, leaving the basic letters by themselves. The rest is explained with inline comments.
var slug = function(str) {
str = str.replace(/^\s+|\s+$/g, ''); // trim
str = str.toLowerCase();
// remove accents, swap ñ for n, etc
var from = "ãàáäâ?èéëêìíïîõòóöôùúüûñç·/_,:;";
var to = "aaaaaeeeeeiiiiooooouuuunc------";
for (var i=0, l=from.length ; i<l ; i++) {
str = str.replace(new RegExp(from.charAt(i), 'g'), to.charAt(i));
}
str = str.replace(/[^a-z0-9 -]/g, '') // remove invalid chars
.replace(/\s+/g, '-') // collapse whitespace and replace by -
.replace(/-+/g, '-'); // collapse dashes
return str;
};
and try
slug($('#field').val())
original by: http://dense13.com/blog/2009/05/03/converting-string-to-slug-javascript/
EDIT: extended for more language specific chars:
var from = "ÁÄÂÀÃÅCÇCDÉEËÈÊ?E?GÍÌÎÏINÑÓÖÒÔÕØRRŠSTÚUÜÙÛÝŸŽáäâàãåcçcdéeëèê?e?gíìîïinñóöòôõøðrršstúuüùûýÿžþÞÐdßÆa·/_,:;";
var to = "AAAAAACCCDEEEEEEEEGIIIIINNOOOOOORRSSTUUUUUYYZaaaaaacccdeeeeeeeegiiiiinnooooooorrsstuuuuuyyzbBDdBAa------";
Yet another one. Short and keeps special characters:
imaginação é mato => imaginacao-e-mato
function slugify (text) {
const a = 'àáäâãèéëêìíïîòóöôùúüûñçßÿœærsn?????u?z?·/_,:;'
const b = 'aaaaaeeeeiiiioooouuuuncsyoarsnpwgnmuxzh------'
const p = new RegExp(a.split('').join('|'), 'g')
return text.toString().toLowerCase()
.replace(/\s+/g, '-') // Replace spaces with -
.replace(p, c =>
b.charAt(a.indexOf(c))) // Replace special chars
.replace(/&/g, '-and-') // Replace & with 'and'
.replace(/[^\w\-]+/g, '') // Remove all non-word chars
.replace(/\-\-+/g, '-') // Replace multiple - with single -
.replace(/^-+/, '') // Trim - from start of text
.replace(/-+$/, '') // Trim - from end of text
}
function slugify(text){
return text.toString().toLowerCase()
.replace(/\s+/g, '-') // Replace spaces with -
.replace(/[^\u0100-\uFFFF\w\-]/g,'-') // Remove all non-word chars ( fix for UTF-8 chars )
.replace(/\-\-+/g, '-') // Replace multiple - with single -
.replace(/^-+/, '') // Trim - from start of text
.replace(/-+$/, ''); // Trim - from end of text
}
*based on https://gist.github.com/mathewbyrne/1280286
now you can transform this string:
Barack_Obama ?????_????? ~!@#$%^&*()+/-+?><:";'{}[]\|`
into:
barack_obama-?????_?????
applying to your code:
$("#Restaurant_Name").keyup(function(){
var Text = $(this).val();
Text = slugify(Text);
$("#Restaurant_Slug").val(Text);
});
I found a good and complete solution for English
function slugify(string) {
return string
.toString()
.trim()
.toLowerCase()
.replace(/\s+/g, "-")
.replace(/[^\w\-]+/g, "")
.replace(/\-\-+/g, "-")
.replace(/^-+/, "")
.replace(/-+$/, "");
}
Some examples of it in use:
slugify(12345);
// "12345"
slugify(" string with leading and trailing whitespace ");
// "string-with-leading-and-trailing-whitespace"
slugify("mIxEd CaSe TiTlE");
// "mixed-case-title"
slugify("string with - existing hyphens -- ");
// "string-with-existing-hyphens"
slugify("string with Special™ characters");
// "string-with-special-characters"
Thanks to Andrew Stewart
First of all, regular expressions should not have surrounding quotes, so '/\s/g' should be /\s/g
In order to replace all non-alphanumerical characters with dashes, this should work (using your example code):
$("#Restaurant_Name").keyup(function(){
var Text = $(this).val();
Text = Text.toLowerCase();
Text = Text.replace(/[^a-zA-Z0-9]+/g,'-');
$("#Restaurant_Slug").val(Text);
});
That should do the trick...
You can use your own function for this.
try it: http://jsfiddle.net/xstLr7aj/
function string_to_slug(str) {
str = str.replace(/^\s+|\s+$/g, ''); // trim
str = str.toLowerCase();
// remove accents, swap ñ for n, etc
var from = "àáäâèéëêìíïîòóöôùúüûñç·/_,:;";
var to = "aaaaeeeeiiiioooouuuunc------";
for (var i=0, l=from.length ; i<l ; i++) {
str = str.replace(new RegExp(from.charAt(i), 'g'), to.charAt(i));
}
str = str.replace(/[^a-z0-9 -]/g, '') // remove invalid chars
.replace(/\s+/g, '-') // collapse whitespace and replace by -
.replace(/-+/g, '-'); // collapse dashes
return str;
}
$(document).ready(function() {
$('#test').submit(function(){
var val = string_to_slug($('#t').val());
alert(val);
return false;
});
});
Hope this can save someone's day...
/* Encode string to slug */_x000D_
function convertToSlug( str ) {_x000D_
_x000D_
//replace all special characters | symbols with a space_x000D_
str = str.replace(/[`~!@#$%^&*()_\-+=\[\]{};:'"\\|\/,.<>?\s]/g, ' ').toLowerCase();_x000D_
_x000D_
// trim spaces at start and end of string_x000D_
str = str.replace(/^\s+|\s+$/gm,'');_x000D_
_x000D_
// replace space with dash/hyphen_x000D_
str = str.replace(/\s+/g, '-'); _x000D_
document.getElementById("slug-text").innerHTML= str;_x000D_
//return str;_x000D_
}
_x000D_
<input type="text" onload="convertToSlug(this.value)" onkeyup="convertToSlug(this.value)" value="Try it Yourself"/>_x000D_
<p id="slug-text"></p>
_x000D_
Take a look at this slug function to sanitize URLs, developed by Sean Murphy at https://gist.github.com/sgmurphy/3095196
/**
* Create a web friendly URL slug from a string.
*
* Requires XRegExp (http://xregexp.com) with unicode add-ons for UTF-8 support.
*
* Although supported, transliteration is discouraged because
* 1) most web browsers support UTF-8 characters in URLs
* 2) transliteration causes a loss of information
*
* @author Sean Murphy <[email protected]>
* @copyright Copyright 2012 Sean Murphy. All rights reserved.
* @license http://creativecommons.org/publicdomain/zero/1.0/
*
* @param string s
* @param object opt
* @return string
*/
function url_slug(s, opt) {
s = String(s);
opt = Object(opt);
var defaults = {
'delimiter': '-',
'limit': undefined,
'lowercase': true,
'replacements': {},
'transliterate': (typeof(XRegExp) === 'undefined') ? true : false
};
// Merge options
for (var k in defaults) {
if (!opt.hasOwnProperty(k)) {
opt[k] = defaults[k];
}
}
var char_map = {
// Latin
'À': 'A', 'Á': 'A', 'Â': 'A', 'Ã': 'A', 'Ä': 'A', 'Å': 'A', 'Æ': 'AE', 'Ç': 'C',
'È': 'E', 'É': 'E', 'Ê': 'E', 'Ë': 'E', 'Ì': 'I', 'Í': 'I', 'Î': 'I', 'Ï': 'I',
'Ð': 'D', 'Ñ': 'N', 'Ò': 'O', 'Ó': 'O', 'Ô': 'O', 'Õ': 'O', 'Ö': 'O', 'O': 'O',
'Ø': 'O', 'Ù': 'U', 'Ú': 'U', 'Û': 'U', 'Ü': 'U', 'U': 'U', 'Ý': 'Y', 'Þ': 'TH',
'ß': 'ss',
'à': 'a', 'á': 'a', 'â': 'a', 'ã': 'a', 'ä': 'a', 'å': 'a', 'æ': 'ae', 'ç': 'c',
'è': 'e', 'é': 'e', 'ê': 'e', 'ë': 'e', 'ì': 'i', 'í': 'i', 'î': 'i', 'ï': 'i',
'ð': 'd', 'ñ': 'n', 'ò': 'o', 'ó': 'o', 'ô': 'o', 'õ': 'o', 'ö': 'o', 'o': 'o',
'ø': 'o', 'ù': 'u', 'ú': 'u', 'û': 'u', 'ü': 'u', 'u': 'u', 'ý': 'y', 'þ': 'th',
'ÿ': 'y',
// Latin symbols
'©': '(c)',
// Greek
'?': 'A', '?': 'B', 'G': 'G', '?': 'D', '?': 'E', '?': 'Z', '?': 'H', 'T': '8',
'?': 'I', '?': 'K', '?': 'L', '?': 'M', '?': 'N', '?': '3', '?': 'O', '?': 'P',
'?': 'R', 'S': 'S', '?': 'T', '?': 'Y', 'F': 'F', '?': 'X', '?': 'PS', 'O': 'W',
'?': 'A', '?': 'E', '?': 'I', '?': 'O', '?': 'Y', '?': 'H', '?': 'W', '?': 'I',
'?': 'Y',
'a': 'a', 'ß': 'b', '?': 'g', 'd': 'd', 'e': 'e', '?': 'z', '?': 'h', '?': '8',
'?': 'i', '?': 'k', '?': 'l', 'µ': 'm', '?': 'n', '?': '3', '?': 'o', 'p': 'p',
'?': 'r', 's': 's', 't': 't', '?': 'y', 'f': 'f', '?': 'x', '?': 'ps', '?': 'w',
'?': 'a', '?': 'e', '?': 'i', '?': 'o', '?': 'y', '?': 'h', '?': 'w', '?': 's',
'?': 'i', '?': 'y', '?': 'y', '?': 'i',
// Turkish
'S': 'S', 'I': 'I', 'Ç': 'C', 'Ü': 'U', 'Ö': 'O', 'G': 'G',
's': 's', 'i': 'i', 'ç': 'c', 'ü': 'u', 'ö': 'o', 'g': 'g',
// Russian
'?': 'A', '?': 'B', '?': 'V', '?': 'G', '?': 'D', '?': 'E', '?': 'Yo', '?': 'Zh',
'?': 'Z', '?': 'I', '?': 'J', '?': 'K', '?': 'L', '?': 'M', '?': 'N', '?': 'O',
'?': 'P', '?': 'R', '?': 'S', '?': 'T', '?': 'U', '?': 'F', '?': 'H', '?': 'C',
'?': 'Ch', '?': 'Sh', '?': 'Sh', '?': '', '?': 'Y', '?': '', '?': 'E', '?': 'Yu',
'?': 'Ya',
'?': 'a', '?': 'b', '?': 'v', '?': 'g', '?': 'd', '?': 'e', '?': 'yo', '?': 'zh',
'?': 'z', '?': 'i', '?': 'j', '?': 'k', '?': 'l', '?': 'm', '?': 'n', '?': 'o',
'?': 'p', '?': 'r', '?': 's', '?': 't', '?': 'u', '?': 'f', '?': 'h', '?': 'c',
'?': 'ch', '?': 'sh', '?': 'sh', '?': '', '?': 'y', '?': '', '?': 'e', '?': 'yu',
'?': 'ya',
// Ukrainian
'?': 'Ye', '?': 'I', '?': 'Yi', '?': 'G',
'?': 'ye', '?': 'i', '?': 'yi', '?': 'g',
// Czech
'C': 'C', 'D': 'D', 'E': 'E', 'N': 'N', 'R': 'R', 'Š': 'S', 'T': 'T', 'U': 'U',
'Ž': 'Z',
'c': 'c', 'd': 'd', 'e': 'e', 'n': 'n', 'r': 'r', 'š': 's', 't': 't', 'u': 'u',
'ž': 'z',
// Polish
'A': 'A', 'C': 'C', 'E': 'e', 'L': 'L', 'N': 'N', 'Ó': 'o', 'S': 'S', 'Z': 'Z',
'Z': 'Z',
'a': 'a', 'c': 'c', 'e': 'e', 'l': 'l', 'n': 'n', 'ó': 'o', 's': 's', 'z': 'z',
'z': 'z',
// Latvian
'A': 'A', 'C': 'C', 'E': 'E', 'G': 'G', 'I': 'i', 'K': 'k', 'L': 'L', 'N': 'N',
'Š': 'S', 'U': 'u', 'Ž': 'Z',
'a': 'a', 'c': 'c', 'e': 'e', 'g': 'g', 'i': 'i', 'k': 'k', 'l': 'l', 'n': 'n',
'š': 's', 'u': 'u', 'ž': 'z'
};
// Make custom replacements
for (var k in opt.replacements) {
s = s.replace(RegExp(k, 'g'), opt.replacements[k]);
}
// Transliterate characters to ASCII
if (opt.transliterate) {
for (var k in char_map) {
s = s.replace(RegExp(k, 'g'), char_map[k]);
}
}
// Replace non-alphanumeric characters with our delimiter
var alnum = (typeof(XRegExp) === 'undefined') ? RegExp('[^a-z0-9]+', 'ig') : XRegExp('[^\\p{L}\\p{N}]+', 'ig');
s = s.replace(alnum, opt.delimiter);
// Remove duplicate delimiters
s = s.replace(RegExp('[' + opt.delimiter + ']{2,}', 'g'), opt.delimiter);
// Truncate slug to max. characters
s = s.substring(0, opt.limit);
// Remove delimiter from ends
s = s.replace(RegExp('(^' + opt.delimiter + '|' + opt.delimiter + '$)', 'g'), '');
return opt.lowercase ? s.toLowerCase() : s;
}
All you needed was a plus :)
$("#Restaurant_Name").keyup(function(){
var Text = $(this).val();
Text = Text.toLowerCase();
var regExp = /\s+/g;
Text = Text.replace(regExp,'-');
$("#Restaurant_Slug").val(Text);
});
function slugify(content) {
return content.toLowerCase().replace(/ /g,'-').replace(/[^\w-]+/g,'');
}
// slugify('Hello World');
// this will return 'hello-world';
this works for me fine.
Found it on CodeSnipper
Well, after reading the answers, I came up with this one.
const generateSlug = (text) => text.toLowerCase().trim().replace(/[^\w- ]+/g, '').replace(/ /g, '-').replace(/[-]+/g, '-');
I create a plugin to implement in most of languages: http://leocaseiro.com.br/jquery-plugin-string-to-slug/
Default Usage:
$(document).ready( function() {
$("#string").stringToSlug();
});
Is very easy has stringToSlug jQuery Plugin
private string ToSeoFriendly(string title, int maxLength) {
var match = Regex.Match(title.ToLower(), "[\\w]+");
StringBuilder result = new StringBuilder("");
bool maxLengthHit = false;
while (match.Success && !maxLengthHit) {
if (result.Length + match.Value.Length <= maxLength) {
result.Append(match.Value + "-");
} else {
maxLengthHit = true;
// Handle a situation where there is only one word and it is greater than the max length.
if (result.Length == 0) result.Append(match.Value.Substring(0, maxLength));
}
match = match.NextMatch();
}
// Remove trailing '-'
if (result[result.Length - 1] == '-') result.Remove(result.Length - 1, 1);
return result.ToString();
}
$("#Restaurant_Name").keyup(function(){
var Text = $(this).val();
Text = Text.toLowerCase();
Text = Text.replace(/[^a-zA-Z0-9]+/g,'-');
$("#Restaurant_Slug").val(Text);
});
This code is working
Source: Stackoverflow.com