How to move this function call out of my HTML code - javascript

I am trying to change a javascript function that uses a inline event handler to one that of one with a more modern approach. I would like to remove the ugly event handler from the actual HTML markup and put it in a modular external javascript file. Here is the test case:
Here is the current code (working fine as far as functionality is concerned
function formatPhone(obj) {
var numbers = obj.value.replace(/\D/g, ''),
char = {0:'(',3:') ',6:' - '};
obj.value = '';
for (var i = 0; i < numbers.length; i++) {
obj.value += (char[i]||'') + numbers[i];
}
}
What I would like to accomplish is something like this:
var TargetEl = $('[name="pnumb"]');
TargetEl.on('blur', function() {
var UserInput = $('[name="pnumb"]').value.replace(/\D/g, ''),
char = {0:'(',3:') ',6:' - '};
TargetEl.value = '';
for (var i = 0; i < UserInput.length; i++) {
TargetEl.value += (char[i]||'') + numbers[i];
}
My main focus is to remove the inline js and onblur="" event handler.I also want have the phone number formatted after the targeted El is blurred. Lastly I want this to be called by simply assigning a class of say .pnumbFormat... (Thanks in advance for your help SO!)
Here is the fiddle ... http://jsfiddle.net/UberNerd/ae4fk/

Modify your function to accept string and return string.
function formatPhone(value) {
var numbers = value.replace(/\D/g, ''),
char = {
0: '(',
3: ') ',
6: ' - '
};
value = '';
for (var i = 0; i < numbers.length; i++) {
value += (char[i] || '') + numbers[i];
}
return value;
}
var TargetEl = $('[name="pnumb"]');
TargetEl.on('blur', function () {
$(this).val(formatPhone($(this).val()))
});
DEMO
Even better
Thanks #KevinB for Great improvisation.
function formatPhone(_,value) {
var numbers = value.replace(/\D/g, ''),
char = {
0: '(',
3: ') ',
6: ' - '
};
value = '';
for (var i = 0; i < numbers.length; i++) {
value += (char[i] || '') + numbers[i];
}
return value;
}
var TargetEl = $('[name="pnumb"]');
TargetEl.on('blur', function () {
$(this).val(formatPhone)
});
DEMO

In your HTML, consider adding an attribute so you know which fields to format.
<input value="22" type="text" name="pnumb" data-formatter="phone" />
Then in your JavaScript, you can select those elements and set up the handlers:
$('input[data-formatter="phone"]').each(function (index, element) {
// Add blur handlers or whatever here.
});
This way, you only need to add that attribute to your markup, and your global JS takes care of it. Much less to hook up on a per-page basis.

Related

Why isn't JavaScript for loop incrementing?

I'm trying to replace the <li> with 1. 2. 3. respectively. I managed to change the <li> to a number, but that number is 0. The loop doesn't want to work. To be honest, this method may be impossible.
Take a look at the Fiddle if you'd like.
This is my function(){...} :
function doIt(){
var input = document.getElementById("input");
var li = /<li>/; // match opening li
var liB = /<\/li>/; // match closing li
var numberOfItems = input.value.match(li).length; // number of lis that occur
for(var i = 0; i < numberOfItems; i++) {
insertNumber(i); // execute insertNumber function w/ parameter of incremented i
}
function insertNumber(number){
input.value = input.value.replace(li, number + "." + " ").replace(liB, "");
}
}
I understand the insertNumber(){...} function is not necessary.
Here's an alternative method, turning your HTML textarea contents into DOM elements that jQuery can manipulate and managing them that way:
function doIt() {
var $domElements = $.parseHTML( $('#input').val().trim() ),
output = [],
i = 1;
$.each($domElements, function(index, element) {
if($(this).text().trim() != '') {
output.push( i + '. ' + $(this).text().trim() );
i++;
}
});
$('#input').val(output.join('\n'));
}

Simulate the look of typing, not the actual keypresses, in javascript

I'm trying to write a simple function that will make it appear as though someone is typing in a textarea
-- This is my function (forgive me if its atrocious, but I don't normally use javascript) ---
The console.log() part works fine, but for some reason I cannot get this script to update the dom the way I would expect...
function type(string) {
value = "";
el = document.getElementById("typeArea");
for (var i = 0; i < string.length; i++) {
value += string[i];
//$("#fbw > textarea").val(value);
el.textContent = value;
console.log(value);
sleep(160);
}
sleep(2000);
}
I appreciate any insight you can give me.
jsFiddle Demo
All you were missing was a construct instead of Sleep. The js approach for accomplishing this is to use a timeout and a recursive call in order to iterate through your string
function type(string,element){
(function writer(i){
if(string.length <= i++){
element.value = string;
return;
}
element.value = string.substring(0,i);
if( element.value[element.value.length-1] != " " )element.focus();
var rand = Math.floor(Math.random() * (100)) + 140;
setTimeout(function(){writer(i);},rand);
})(0)
}
You can do something like this using setTimeout function.
Codepen
$(function(){
simulateTyping('looks like someone is typing...', '#txt')
function simulateTyping(str, textAreaId) {
var textArea = $(textAreaId);
var currentCharIndex = 0;
function typeChar(){
if (currentCharIndex >= str.length)
return;
var char = str[currentCharIndex];
textArea.val(textArea.val() + char);
currentCharIndex ++;
setTimeout(typeChar, 500);
}
typeChar();
}
})

two delimiters output formatting javascript

I thought this would be easier, but running into a weird issue.
I want to split the following:
theList = 'firstword:subwordone;subwordtwo;subwordthree;secondword:subwordone;thirdword:subwordone;subwordtwo;';
and have the output be
firstword
subwordone
subwordtwo
subwordthree
secondword
subwordone
thirdword
subwordone
subwordtwo
The caveat is sometimes the list can be
theList = 'subwordone;subwordtwo;subwordthree;subwordfour;'
ie no ':' substrings to print out, and that would look like just
subwordone
subwordtwo
subwordthree
subwordfour
I have tried variations of the following base function, trying recursion, but either get into infinite loops, or undefined output.
function getUl(theList, splitOn){
var r = '<ul>';
var items = theList.split(splitOn);
for(var li in items){
r += ('<li>'+items[li]+'</li>');
}
r += '</ul>';
return r;
}
The above function is just my starting point and obviously doesnt work, just wanted to show what path I am going down, and to be shown the correct path, if this is totally off base.
It seems you need two cases, and the difference between the two is whether there is a : in your string.
if(theList.indexOf(':') == -1){
//Handle the no sublist case
} else {
//Handle the sublist case
}
Starting with the no sublist case, we develop the simple pattern:
var elements = theList.split(';');
for(var i = 0; i < elements.length; i++){
var element = elements[i];
//Add your element to your list
}
Finally, we apply that same pattern to come up with the implementation for the sublist case:
var elements = theList.split(';');
for(var i = 0; i < elements.length; i++){
var element = elements[i];
if(element.indexOf(':') == -1){
//Add your simple element to your list
} else {
var innerElements = element.split(':');
//Add innerElements[0] as your parent element
//Add innerElements[1] as your child element
//Increment i until you hit another element with ':', adding the single elements each increment as child elements.
//Decrement i so it considers the element with the ':' as a parent element.
}
}
Keep track of the current list to add items to, and create a new list when you find a colon in an item:
var baseParent = $('ul'), parent = baseParent;
$.each(theList.split(';'), function(i, e) {
if (e.length) {
var p = e.split(':');
if (p.length > 1) {
baseParent.append($('<li>').append($('<span>').text(p[0])).append(parent = $('<ul>')));
}
parent.append($('<li>').text(p[p.length - 1]));
}
});
Demo: http://jsfiddle.net/Guffa/eWQpR/
Demo for "1;2;3;4;": http://jsfiddle.net/Guffa/eWQpR/2/
There's probably a more elegant solution but this does the trick. (See edit below)
function showLists(text) {
// Build the lists
var lists = {'': []};
for(var i = 0, listKey = ''; i < text.length; i += 2) {
if(text[i + 1] == ':') {
listKey = text[i];
lists[listKey] = [];
} else {
lists[listKey].push(text[i]);
}
}
// Show the lists
for(var listName in lists) {
if(listName) console.log(listName);
for(var j in lists[listName]) {
console.log((listName ? ' ' : '') + lists[listName][j]);
}
}
}
EDIT
Another interesting approach you could take would be to start by breaking it up into sections (assuming text equals one of the examples you gave):
var lists = text.match(/([\w]:)?([\w];)+/g);
Then you have broken down the problem into simpler segments
for(var i = 0; i < lists.length; i++) {
var listParts = lists[i].split(':');
if(listParts.length == 1) {
console.log(listParts[0].split(';').join("\n"));
} else {
console.log(listParts[0]);
console.log(' ' + listParts[1].split(';').join("\n "));
}
}
The following snippet displays the list depending on your requirements
var str = 'subwordone;subwordtwo;subwordthree;';
var a = []; var arr = [];
a = str;
var final = [];
function split_string(a){
var no_colon = true;
for(var i = 0; i < a.length; i++){
if(a[i] == ':'){
no_colon = false;
var temp;
var index = a[i-1];
var rest = a.substring(i+1);
final[index] = split_string(rest);
return a.substring(0, i-2);
}
}
if(no_colon) return a;
}
function display_list(element, index, array) {
$('#results ul').append('<li>'+element+'</li>');
}
var no_colon_string = split_string(a).split(';');
if(no_colon_string){
$('#results').append('<ul><ul>');
}
no_colon_string.forEach(display_list);
console.log(final);
working fiddle here

Capitalise the first letter of each word within a certain class

Is it possible to capitalise the first letter of each word in a certain class name using jQuery / javascript? I just want to capitalise the first letter of each word of all the fields marked with the class 'capital'.
I just want it to do it as they type, and I know you can do it with css but this is no good as it is stored in the DB as lowercase still.
Here's a simple jQuery plugin that could do this for you:
$.fn.capitalise = function() {
return this.each(function() {
var $this = $(this),
text = $this.text(),
tokens = text.split(" ").filter(function(t) {return t != ""; }),
res = [],
i,
len,
component;
for (i = 0, len = tokens.length; i < len; i++) {
component = tokens[i];
res.push(component.substring(0, 1).toUpperCase());
res.push(component.substring(1));
res.push(" "); // put space back in
}
$this.text(res.join(""));
});
};
And then call like:
$(".myClass").capitalise();
Here's a working example.
The solution is something like this:
Working Sample: http://jsfiddle.net/Py7rW/7/
$('.captial').each(function(){
var arr = $(this).text().split(' ');
var result = "";
for (var x=0; x<arr.length; x++)
result+=arr[x].substring(0,1).toUpperCase()+arr[x].substring(1)+' ';
$(this).text(result.substring(0, result.length-1));
});
I think this will work :)
$('.capital').css("text-transform","capitalize");
You can try something like:
$('.capital').each(function() {
var s = $(this).text().split(' ');
for(var i=0; i<s.length; i++) {
s[i] = s[i].substring(0,1).toUpperCase() + s[i].substring(1);
}
s = s.join(' ');
$(this).text(s);
}
I would use the css text-transform:capitalize to avoid having to run this on every keypress,
and change the actual value of the fields on change.
field.value= field.value.replace(/((^| )[a-z])/g, function(a, b){
return b.toUpperCase();
});
Simple Step to capitalize the first letter of each word :
$(document).on('keyup', '#myText', function () {
this.value = this.value.replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();});
});
You could do something like this. This will capitalize the text in a textbox whenever the text has changed:
$(document).ready(function() {
$('.capital').change(function() {
var arr = $(this).val().split(' ');
var result = "";
for (var i=0; i<arr.length; i++){
result += arr[i].substring(0,1).toUpperCase() + arr[i].substring(1);
if (i < arr.length-1) {
result += ' ';
}
}
$(this).val(result);
})
});
You can see a working fiddle here: http://jsfiddle.net/5dMg7/

getElementsByClassName IE resolution issue

I am having issues figuring out how to resolve the getElementsByClassName issue in IE. How would I best implement the robert nyman (can't post the link to it since my rep is only 1) resolution into my code? Or would a jquery resolution be better? my code is
function showDesc(name) {
var e = document.getElementById(name);
//Get a list of elements that have a class name of service selected
var list = document.getElementsByClassName("description show");
//Loop through those items
for (var i = 0; i < list.length; ++i) {
//Reset all class names to description
list[i].className = "description";
}
if (e.className == "description"){
//Set the css class for the clicked element
e.className += " show";
}
else{
if (e.className == "description show"){
return;
}
}}
and I am using it on this page dev.msmnet.com/services/practice-management to show/hide the description for each service (works in Chrome and FF). Any tips would be greatly appreciated.
I was curious to see what a jQuery version of your function would look like, so I came up with this:
function showDesc(name) {
var e = $("#" + name);
$(".description.show").removeClass("show");
if(e.attr("class") == "description") {
e.addClass("show");
} else if(e.hasClass("description") && e.hasClass("show")) {
return;
}
}
This should support multiple classes.
function getElementsByClassName(findClass, parent) {
parent = parent || document;
var elements = parent.getElementsByTagName('*');
var matching = [];
for(var i = 0, elementsLength = elements.length; i < elementsLength; i++){
if ((' ' + elements[i].className + ' ').indexOf(findClass) > -1) {
matching.push(elements[i]);
}
}
return matching;
}
You can pass in a parent too, to make its searching the DOM a bit faster.
If you want getElementsByClassName('a c') to match HTML <div class="a b c" /> then try changing it like so...
var elementClasses = elements[i].className.split(/\s+/),
matchClasses = findClass.split(/\s+/), // Do this out of the loop :)
found = 0;
for (var j = 0, elementClassesLength = elementClasses.length; j < elementClassesLength; j++) {
if (matchClasses.indexOf(elementClasses[j]) > -1) {
found++;
}
}
if (found == matchClasses.length) {
// Push onto matching array
}
If you want this function to only be available if it doesn't already exist, wrap its definition with
if (typeof document.getElementsByClassName != 'function') { }
Even easier jQuery solution:
$('.service').click( function() {
var id = "#" + $(this).attr('id') + 'rt';
$('.description').not(id).hide();
$( id ).show();
}
Why bother with a show class if you are using jQuery?
Heres one I put together, reliable and possibly the fastest. Should work in any situation.
function $class(className) {
var children = document.getElementsByTagName('*') || document.all;
var i = children.length, e = [];
while (i--) {
var classNames = children[i].className.split(' ');
var j = classNames.length;
while (j--) {
if (classNames[j] == className) {
e.push(children[i]);
break;
}
}
}
return e;
}
I used to implement HTMLElement.getElementByClassName(), but at least Firefox and Chrome, only find the half of the elements when those elements are a lot, instead I use something like (actually it is a larger function):
getElmByClass(clm, parent){
// clm: Array of classes
if(typeof clm == "string"){ clm = [clm] }
var i, m = [], bcl, re, rm;
if (document.evaluate) { // Non MSIE browsers
v = "";
for(i=0; i < clm.length; i++){
v += "[contains(concat(' ', #"+clc+", ' '), ' " + base[i] + " ')]";
}
c = document.evaluate("./"+"/"+"*" + v, parent, null, 5, null);
while ((node = c.iterateNext())) {
m.push(node);
}
}else{ // MSIE which doesn't understand XPATH
v = elm.getElementsByTagName('*');
bcl = "";
for(i=0; i < clm.length; i++){
bcl += (i)? "|":"";
bcl += "\\b"+clm[i]+"\\b";
}
re = new RegExp(bcl, "gi");
for(i = 0; i < v.length; i++){
if(v.className){
rm = v[i].className.match(bcl);
if(rm && rm.length){ // sometimes .match returns an empty array so you cannot use just 'if(rm)'
m.push(v[i])
}
}
}
}
return m;
}
I think there would be a faster way to iterate without XPATH, because RegExp are slow (perhaps a function with .indexOf, it shuld be tested), but it is working well
You can replace getElementsByClassName() with the following:
function getbyclass(n){
var elements = document.getElementsByTagName("*");
var result = [];
for(z=0;z<elements.length;z++){
if(elements[z].getAttribute("class") == n){
result.push(elements[z]);
}
}
return result;
}
Then you can use it like this:
getbyclass("description") // Instead of document.getElementsByClassName("description")

Categories