[JS]Manually trimLeft of all whitespace in a string - javascript

I've been trying to create a trimLeft() function and I seem to be drawing blanks on the logic.
I'm not allowed to use built in functions like replace, splice, substr, substring, trim or toString.
I've been rolling with this:
function trimLeft(string){
var noSpace = '';
for(var o = 0; o < string.length; o++){
if (string.charAt(o) !== ' ' && string.charAt(o) !== "\t"){
noSpace += string.charAt(o);
}
}
return noSpace;
}
window.alert(trimLeft("|" + "\tAPPLE\t" + "|"));
However it removes all whitespace rather than just the left side. Any help would be appreciated.

Here's a working solution:
function trimLeft(string) {
for (var o = 0; o < string.length; o++){
if (string.charAt(o) !== ' ' && string.charAt(o) !== "\t"){
return string.substring(o);
}
}
}
window.alert("|" + trimLeft("\tAPPLE\t") + "|");
Note that as soon as a non-whitespace character is found, the rest of the string is returned immediately.
Also note that the test using window.alert() didn't work, because you tested the function with the string "|\tAPPLE\t|", which has no leading whitespace.
Here's a solution wihtout using any built-in string functions:
function trimLeft(string) {
var noSpace = "";
var isStart = true; // becomes false as soon as non-whitespace is found
for (var o = 0; o < string.length; o++) {
if (!isStart || (string[o] !== " " && string[o] !== "\t")) {
noSpace += string[o];
isStart = false;
}
}
return noSpace;
}
window.alert("|" + trimLeft("\tAPPLE\t") + "|");
window.alert("|" + trimLeft(" BANANA ") + "|");

Related

Can I perform "Copy Selector" with javascript without using Chrome Dev tools [duplicate]

I am moving elements using javascript and I need to create a logic for the combinations happening during the drag/drops
I'm trying to get details from the elements, a CSS like selector could be also good, but dunno if it is possible.. (like copy-selector in chrome dev tools)
document.onmouseup = function(e){
targetDest = e.target;
//console.log('targetDest: ', targetDest);
let
indexA = Array.from(targetCurr.parentNode.children).indexOf(targetCurr),
indexB = Array.from(targetDest.parentNode.children).indexOf(targetDest);
console.log(indexA, indexB);
if(targetDest != targetCurr){
if(targetDest == document.documentElement){
console.log('document');
}
else if(targetDest == undefined){
console.log('undefined');
}
else if(!targetDest){
console.log('!dest');
}
else if(targetDest == null){
console.log('null');
}
else if(targetDest == false){
console.log('false');
}
else{
console.log('else');
//targetCurr.parentNode.insertBefore(targetDest, targetCurr);
//console.log('...');
}
}else{
console.log('itself');
}
}
Keep in mind that this will not necessarily uniquely identify elements. But, you can construct that type of selector by traversing upwards from the node and prepending the element you're at. You could potentially do something like this
var generateQuerySelector = function(el) {
if (el.tagName.toLowerCase() == "html")
return "HTML";
var str = el.tagName;
str += (el.id != "") ? "#" + el.id : "";
if (el.className) {
var classes = el.className.split(/\s/);
for (var i = 0; i < classes.length; i++) {
str += "." + classes[i]
}
}
return generateQuerySelector(el.parentNode) + " > " + str;
}
var qStr = generateQuerySelector(document.querySelector("div.moo"));
alert(qStr);
body
<div class="outer">
div.outer
<div class="inner" id="foo">
div#foo.inner
<div class="moo man">
div.moo.man
</div>
</div>
</div>
I wouldn't suggest using this for much besides presenting the information to a user. Splitting it up and reusing parts are bound to cause problems.
My solution using :nth-child:
function getSelector(elm)
{
if (elm.tagName === "BODY") return "BODY";
const names = [];
while (elm.parentElement && elm.tagName !== "BODY") {
if (elm.id) {
names.unshift("#" + elm.getAttribute("id")); // getAttribute, because `elm.id` could also return a child element with name "id"
break; // Because ID should be unique, no more is needed. Remove the break, if you always want a full path.
} else {
let c = 1, e = elm;
for (; e.previousElementSibling; e = e.previousElementSibling, c++) ;
names.unshift(elm.tagName + ":nth-child(" + c + ")");
}
elm = elm.parentElement;
}
return names.join(">");
}
var qStr = getSelector(document.querySelector("div.moo"));
alert(qStr);
body
<div class="outer">
div.outer
<div class="inner" id="foo">
div#foo.inner
<div class="moo man">
div.moo.man
</div>
</div>
</div>
Please note it won't return the whole path if there's an element with ID in it - every ID should be unique on the page, as valid HTML requires.
I use output of this function in document.querySelector later in the code, because I needed to return focus to the same element after replaceChild of its parent element.
I hope CollinD won't mind I borrowed his markup for the code snippet :-)
I mixed the 2 solutions proposed to have a result readable by humans and which gives the right element if there are several similar siblings:
function elemToSelector(elem) {
const {
tagName,
id,
className,
parentNode
} = elem;
if (tagName === 'HTML') return 'HTML';
let str = tagName;
str += (id !== '') ? `#${id}` : '';
if (className) {
const classes = className.split(/\s/);
for (let i = 0; i < classes.length; i++) {
str += `.${classes[i]}`;
}
}
let childIndex = 1;
for (let e = elem; e.previousElementSibling; e = e.previousElementSibling) {
childIndex += 1;
}
str += `:nth-child(${childIndex})`;
return `${elemToSelector(parentNode)} > ${str}`;
}
Test with:
// Select an element in Elements tab of your navigator Devtools, or replace $0
document.querySelector(elemToSelector($0)) === $0 &&
document.querySelectorAll(elemToSelector($0)).length === 1
Which might give you something like, it's a bit longer but it's readable and it always works:
HTML > BODY:nth-child(2) > DIV.container:nth-child(2) > DIV.row:nth-child(2) > DIV.col-md-4:nth-child(2) > DIV.sidebar:nth-child(1) > DIV.sidebar-wrapper:nth-child(2) > DIV.my-4:nth-child(1) > H4:nth-child(3)
Edit: I just found the package unique-selector
Small improvement of the #CollinD answer :
1/ Return value when the selector is unique
2/ Trim classes value (classes with end blanks make errors)
3/ Split multiple spaces between classes
var getSelector = function(el) {
if (el.tagName.toLowerCase() == "html")
return "html";
var str = el.tagName.toLowerCase();
str += (el.id != "") ? "#" + el.id : "";
if (el.className) {
var classes = el.className.trim().split(/\s+/);
for (var i = 0; i < classes.length; i++) {
str += "." + classes[i]
}
}
if(document.querySelectorAll(str).length==1) return str;
return getSelector(el.parentNode) + " > " + str;
}
Based on previous solutions, I made a typescript solution with a shorter selector and additional checks.
function elemToSelector(elem: HTMLElement): string {
const {
tagName,
id,
className,
parentElement
} = elem;
let str = '';
if (id !== '' && id.match(/^[a-z].*/)) {
str += `#${id}`;
return str;
}
str = tagName;
if (className) {
str += '.' + className.replace(/(^\s)/gm, '').replace(/(\s{2,})/gm, ' ')
.split(/\s/).join('.');
}
const needNthPart = (el: HTMLElement): boolean => {
let sib = el.previousElementSibling;
if (!el.className) {
return true;
}
while (sib) {
if (el.className !== sib.className) {
return false;
}
sib = sib.previousElementSibling;
}
return false;
}
const getNthPart = (el: HTMLElement): string => {
let childIndex = 1;
let sib = el.previousElementSibling;
while (sib) {
childIndex++;
sib = sib.previousElementSibling;
}
return `:nth-child(${childIndex})`;
}
if (needNthPart(elem)) {
str += getNthPart(elem);
}
if (!parentElement) {
return str;
}
return `${elemToSelector(parentElement)} > ${str}`;
}

How do I fix these JSLint errors?

I'm trying to fix some errors according to JSLint and there are some errors I can't get rid of:
line 46 character 13
Missing 'new'.
SortPosts(categories[categoryNumber].textContent);
line 65 character 16
Unexpected '('.
if (typeof (Storage) !== "undefined") {
line 65 character 9Unexpected 'typeof'. Use '===' to compare directly with undefined.
if (typeof (Storage) !== "undefined") {
This is my code:
window.onload = function () {
"use strict";
var categories,
value,
i,
j,
article,
li,
postsAvailable = false;
function removeElementsByClass() {
var elements = document.getElementsByClassName("forumPost");
while (elements.length > 0) {
elements[0].parentNode.removeChild(elements[0]);
}
}
function addRow(input) {
article = document.createElement('article');
article.className = 'forumPost';
article.innerHTML = "<div id='imageDiv'><img src='" + input.avatar + "' alt='Avatar'id='imageHTML'/><div id='personDiv'> " + input.firstName + " " + input.lastName + " (" + input.age + ") </div><div id='workDiv'>" + input.work + " </div></div><div id='headerDiv'>" + input.firstName + " schreef om <div id='dateDiv'>" + input.date + "</div></div></br><div id='postDiv'>" + input.post + "</div></article>";
document.getElementById('content').appendChild(article);
}
function SortPosts(category) {
if (localStorage.getItem(category) === "") {
alert("No posts found!");
} else {
removeElementsByClass();
if (category !== "Algemeen") {
value = JSON.parse(localStorage.getItem(category));
for (i = 0; i < value.length; ++i) {
addRow(value[i]);
}
} else {
for (i = 0; i < localStorage.length; ++i) {
if (localStorage.getItem(localStorage.key(i)) !== "") {
value = JSON.parse(localStorage.getItem(localStorage.key(i)));
for (j = 0; j < value.length; ++j) {
addRow(value[j]);
}
}
}
}
}
}
function getPostSorter(categoryNumber) {
return function () {
SortPosts(categories[categoryNumber].textContent);
};
}
document.getElementById("newCategoryButton").onclick = function () {
var input = document.getElementById("newCategoryInput").value;
if (input !== "") {
if (localStorage.getItem(input) !== null) {
alert("Categorie bestaat al!");
} else {
li = document.createElement('li');
li.innerHTML = "<a href='#'>" + input + "</a></li>";
localStorage.setItem(input, "");
document.getElementById("categories").appendChild(li);
}
} else {
alert("Uw invoer mag niet leeg zijn!");
}
document.getElementById("newCategoryInput").value = "";
};
if (typeof (Storage) !== "undefined") {
for (i = 0; i < localStorage.length; ++i) {
if (localStorage.getItem(localStorage.key(i)) !== "") {
postsAvailable = true;
break;
}
}
if (postsAvailable === false) {
article = document.createElement('article');
article.className = 'infobox';
article.innerHTML = "<header><h2>Geen posts gevonden!</h2></header><section><p>Ga naar het forum toe om een onderwerp toe te voegen</p></section></article>";
document.getElementById('content').appendChild(article);
} else {
for (i = 0; i < localStorage.length; ++i) {
if (localStorage.getItem(localStorage.key(i)) !== "") {
value = JSON.parse(localStorage.getItem(localStorage.key(i)));
for (j = 0; j < value.length; ++j) {
addRow(value[j]);
}
}
}
}
for (i = 0; i < localStorage.length; ++i) {
li = document.createElement('li');
li.className = "category";
li.innerHTML = "<a href='#'>" + localStorage.key(i) + "</a></li>";
document.getElementById("categories").appendChild(li);
}
categories = document.getElementsByClassName("category");
for (i = 0; i < categories.length; ++i) {
categories[i].addEventListener("click", getPostSorter(i));
}
} else {
alert("Uw browser ondersteunt geen localStorage. Gebruik alstublieft een nieuwere browser zoals Google Chrome, Mozilla Firefox of > Internet Explorer 7");
}
};
Could anyone tell me why these are wrong and what I should do to fix them? I've googled some more information but I wasn't able to actually find anything helpful. I've tried adding "new" to the SortPosts but then it gave me the message Do not use 'new' for side effects.
Missing 'new'.
SortPosts(categories[categoryNumber].textContent);
Functions with names starting with capital letters are, by convention, constructor functions. Yours is not. Rename the function to sortPosts.
Unexpected '('.
if (typeof (Storage) !== "undefined") {
typeof is an operator, not a function.
Use typeof Storage not typeof (Storage).
line 65 character 9Unexpected 'typeof'. Use '===' to compare directly with undefined.
Don't use typeof. Use Storage !== undefined.
NB: JSLint is a very opinionated linter. It is possible for undefined to be overridden. I prefer the typeof approach.
To fix these issues, change line 45 to this:
newSortPosts(categories[categoryNumber].textContent);
Renaming SortPosts to newSortPosts in your code.
On line 65 you don't need to compare the typeof, rather you can just see if Storage is undefined:
if (Storage !== undefined) {
Just a note you when using typeof, you don't need parentheses as typeof is an operator: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/typeof

Ensuring that an entered name doesn’t end with a space

I am trying to get it so that if I type in a name that ends with a space, the textfield will go red. Most of the code works its just one method does not seem to be working.
The issue must be somewhere in the last index part?
var NamePass = true;
function ValidateName() {
var BlankPass = true;
var GreaterThan6Pass = true;
var FirstBlankPass = true;
var BlankMiddleName = true;
if (document.getElementById('Name').value == "") {
BlankPass = false;
}
var Size = document.getElementById('Name').value.length;
console.log("Size = " + Size);
if (Size < 7) {
GreaterThan6Pass = false;
}
if (document.getElementById('Name').value.substring(0, 1) == " ") {
FirstBlankPass = false;
}
var LastIndex = document.getElementById('Name').value.lastIndexOf();
if (document.getElementById('Name').value.substring((LastIndex - 1), 1) == " ") {
FirstBlankPass = false;
}
string = document.getElementById('Name').value;
chars = string.split(' ');
if (chars.length > 1) {} else
BlankMiddleName = false;
if (BlankPass == false || GreaterThan6Pass == false || FirstBlankPass == false || BlankMiddleName == false) {
console.log("BlankPass = " + BlankPass);
console.log("GreaterThan6Pass = " + GreaterThan6Pass);
console.log("FirstBlankPass = " + FirstBlankPass);
console.log("BlankMiddleName = " + BlankMiddleName);
NamePass = false;
document.getElementById('Name').style.background = "red";
} else {
document.getElementById('Name').style.background = "white";
}
}
http://jsfiddle.net/UTtxA/10/
lastIndexOf gets the last index of a character, not the last index in a string. I think you meant to use length instead:
var lastIndex = document.getElementById('Name').value.length;
Another problem with that, though, is that substring takes a start and end index, not a start index and a substring length. You could use substr instead, but charAt is easier:
if (document.getElementById('Name').value.charAt(lastIndex - 1) == " ") {
FirstBlankPass = false;
}
Now, for some general code improvement. Instead of starting with all your variables at true and conditionally setting them to false, just set them to the condition:
var NamePass = true;
function ValidateName() {
var value = document.getElementById('Name').value;
var BlankPass = value == "";
var GreaterThan6Pass = value.length > 6;
var FirstBlankPass = value.charAt(0) == " ";
var LastBlankPass = value.charAt(value.length - 1) == " ";
var BlankMiddleName = value.split(" ").length <= 1;
if (BlankPass || GreaterThan6Pass || FirstBlankPass || LastBlankPass || BlankMiddleName) {
console.log("BlankPass = " + BlankPass);
console.log("GreaterThan6Pass = " + GreaterThan6Pass);
console.log("FirstBlankPass = " + FirstBlankPass);
console.log("BlankMiddleName = " + BlankMiddleName);
NamePass = false;
document.getElementById('Name').style.background = "red";
} else {
document.getElementById('Name').style.background = "white";
}
}
A couple more points of note:
It’s probably a good idea to use camelCase variable names instead of PascalCase ones, the latter usually being reserved for constructors
blah == false should really be written as !blah
An empty if followed by an else can also be replaced with if (!someCondition)
That function looks like it should return true or false, not set the global variable NamePass
Penultimately, you can sum this all up in one regular expression, but if you intend to provide more specific error messages to the user based on what’s actually wrong, then I wouldn’t do that.
function validateName() {
return /^(?=.{6})(\S+(\s|$)){2,}$/.test(document.getElementById('name').value);
}
And finally — please keep in mind that not everyone has a middle name, or even a name longer than 6 characters, as #poke points out.

Trying to add and remove items from an array

The script works by asking user for add or remove an item in the array. Then asks to continue this loop. The problem here is that my script doesn't seem to match my user's input (removeItem) to the item in the list (myList[i]). I'm at a lost as to why this is failing to match.
// new method for removing specific items from a list
Array.prototype.remove = function(from,to) {
var rest = this.slice((to || from) + 1 || this.length);
this.length = from < 0 ? this.length + from : from;
return this.push.apply(this, rest);
};
printList = function() {
var listLength = myList.length;
for (i = 0; i < listLength; i++) {
document.write(i + ":");
document.write(myList[i] + "<br/>");
};
document.write("<br/><br/>");
};
// initial list
var myList = new Array ();
if (myList.length === 0) {
document.write("I have " + myList.length + " item in my list. It is: <br/>");
}
else {
document.write("I have " + myList.length + " items in my list. They are: <br/>");
}
printList();
var continueAdding = "yes";
var askToContinue = "";
while (continueAdding === "yes") {
// loop
var askUser = prompt("What do you want to [A]dd or [R]emove an item to your inventory?").toUpperCase();
switch (askUser) {
case "A": { // add an user specified item to the list
var addItem = prompt("Add something to the list");
myList.push(addItem);
printList();
break;
}
case "R": { // remove an user specified item from the list
var removeItem = prompt("what do you want to remove?");
var listLength = myList.length;
for (i = 0; i < listLength; i++) {
if (removeItem === myList[i]) {
document.write("I found your " + removeItem + " and removed it.<br/>");
myList.remove(i);
}
else {
document.write(removeItem + " does not exist in this list.<br/>");
break;
}
if (myList.length === 0) {
myList[0] = "Nada";
}
};
printList();
break;
}
default: {
document.write("That is not a proper choice.");
}
};
askToContinue = prompt("Do you wish to continue? [Y]es or [N]o?").toUpperCase(); // ask to continue
if (askToContinue === "Y") {
continueAdding = "yes";
}
else {
continueAdding = "no";
}
}
Your loop never allows it to loop through all the items, because it breaks on the first iteration if the item doesn't match.
The break statement should be in the if block, not in the else block - use this instead:
for (i = 0; i < listLength; i++) {
if (removeItem === myList[i]) {
document.write("I found your " + removeItem + " and removed it.<br/>");
myList.remove(i);
break;
}
else {
document.write(removeItem + " does not exist in this list.<br/>");
}
};
if (myList.length === 0) {
myList[0] = "Nada";
}
Also, note that it's looking for an exact match, case sensitive, same punctuation, and everything. If you want it to be a little more lenient you'll need to modify the script to convert both strings to lowercase and strip punctuation before comparing them.
Edit: Just noticed something else -- testing for an empty list needs to be done outside the loop. I updated the above code to reflect this.

Implementing snippets in a textarea

I'm trying to implement snippets into an html textarea. You write a certain word and it will look through key value object and expand the text if it exists. Here is what I have done:
var textarea = document.getElementById("whatever");
var snippets = {
'hello': 'Hello and welcome to my great site'
}
var prepend = "";
var checkCaps = function(e){
if (e.keyCode != 9) return;
e.preventDefault();
var string = "";
var pos = textarea.selectionStart;
var text = textarea.value.split("");
while (pos) {
char = text.pop(pos);
prepend = (char == " ") ? " ": "";
if (char == " ") break;
string += char
pos -= 1;
}
if (snippets[string.reverse()]) {
textarea.value = text.join("")
textarea.value += prepend + snippets[string.reverse()]
}
}
textarea.addEventListener("keydown", checkCaps, false);
String.prototype.reverse=function(){return this.split("").reverse().join("");}
http://jsfiddle.net/JjTmd/
The problem is that the snippet only works in the last word of the textarea's value, and I can't seem to pinpoint where the problem is.
Array.pop doesn't accept a parameter. It removes and returns the last item from the array. Use splice to remove an item at a particular index.
I modified your function as follows and it seems to have the desired behavior:
var checkCaps = function(e){
if (e.keyCode != 9) return;
e.preventDefault();
var string = "";
var pos = textarea.selectionStart;
var text = textarea.value.split("");
while (pos) {
char = text.splice(pos-1,1);
prepend = (char == " ") ? " ": "";
if (char == " ") break;
string += char
pos -= 1;
}
if (snippets[string.reverse()]) {
var start = text.splice(0, pos);
var end = text.splice(pos + string.length);
textarea.value = start.join("") + snippets[string.reverse()] + prepend + text.join("") + end.join("");
}
}
http://jsfiddle.net/JjTmd/1/
Also, you'll probably want to check for new line characters as well:
while (pos) {
char = text.splice(pos-1,1);
if (char == " " || char == "\n") {
prepend = char;
break;
}
string += char
pos -= 1;
}
http://jsfiddle.net/JjTmd/2/

Categories