Can not delete an element of array in JavaScript - javascript

I have a part of code in which the function removeClass must delete an element of array in case, if some of his elements coincides with input parameters.
But it does not work.
var obj = {
className: 'open menu'
};
function removeClass(obj, cls) {
var arr = obj.className.split(' ');
for (var i = 0; i < arr.Length; i++) {
if (cls == arr[i]) delete arr[i]
}
obj.className = arr.join(' ');
return obj.className;
}
console.log(removeClass(obj, 'open'));
// desired output obj.className='menu'
// actual output 'open menu'

You can use Array.prototype.filter() method for this.
var obj = {
className: 'open menu'
};
function removeClass(obj, cls) {
var arr = obj.className.split(' ');
obj.className = arr.filter(function(item) {
return cls !== item;
}).join(' ')
return obj.className;
}
console.log(removeClass(obj, 'open'));
In your code, you have used arr.Length. Actual syntax is arr.length. But even if you use fix your code then, it will not delete the item instead put undefined on its place, then you have to handle extra white spaces. That's why I think above solution is good.

Try this:
var obj = {
className: 'open menu dogs cats'
};
function removeClass(obj, cls) {
return (obj.className = obj.className.split(' ').filter(item => cls !== item).join(' '));
}
console.log(removeClass(obj, 'open'));
console.log(removeClass(obj, 'dogs'));
But if you are trying to do this to a DOM element then use classList instead.
var el = document.getElementById("mine");
console.log(el.className);
el.classList.remove('open');
console.log(el.className);
el.classList.remove('dogs');
console.log(el.className);
<div id="mine" class="menu open dogs cats"></div>

Why just don't replace the string?
var obj = {
className: 'open menu'
};
function removeClass(obj, cls) {
return obj.className = obj.className.replace(cls, '').trim()
}
console.log(removeClass(obj, 'open')); // obj.className='menu'
Also while working with DOM there are already methods for dooing this
const b = document.querySelector('button')
b.addEventListener('click', () => {
b.classList.remove('blue')
})
.red {
color: red
}
.blue {
color: blue
}
<button class="red blue">test</button>

With ES6 you can even make this really short
function removeClass(element, classToRemove) {
return element.className.split(' ')
.filter(class => class !== classToRemove)
.join(' ');
}
If you want to modify the element itself, just reassign the return value to element.className

Related

Uncaught ReferenceError for filter group items

This is all my code.
This is all my code. I want to know. Why do I want to run all the filters together? I'm having trouble.
function customFilter(list, field, value){
return list.filter(item=> {
if(typeof(filter) === 'Object'){
value.foreach(val => {
if (item[field] === value){
return item[field] === value
}
});
}
return item[field] === value
});
}
function checkedInputs() {
return [...querySelector('.filter_type .customCheck')].map((c) => c.checked);
}
let filterCheckboxes = document.querySelectorAll('.customCheck');
filterCheckboxes.forEach(checkbox =>checkbox.addEventListener('change' , (e) =>{
e.preventDefault();
var checkboxes = checkedInputs();
var filteredList = FlyList;
checkboxes.forEach(checkbox => {
let filterTypeElement = findFilterTypeElement(checkbox);
if (filterTypeElement) {
let field = filterTypeElement.getAttribute('data-field');
let val = e.target.value;
console.log(field,val);
filteredList = customFilter(filteredList, field , val);
}
});
}));
function getParents(el, parentSelector /* optional */) {
// If no parentSelector defined will bubble up all the way to *document*
if (parentSelector === undefined) {
parentSelector = document;
}
var parents = [];
var p = el.parentNode;
while (p && (p !== parentSelector || p.parentNode)) {
var o = p;
parents.push(o);
p = o.parentNode;
}
parents.push(parentSelector); // Push that parentSelector you wanted to stop at
return parents;
}
function findFilterTypeElement(el)
{
var result = null;
var parents = getParents(el);
parents.forEach((item) => {
if(hasClass(item,'filter_type') && result == null)
{
result = item;
}
});
return result;
}
function hasClass(element, className) {
return (' ' + element.className + ' ').indexOf(' ' + className + ' ') > -1;
}
To make you understand. I got a picture of my code error.
Uncaught ReferenceError: querySelector is not defined at checkedInputs (functions.js:1013) at HTMLLabelElement.checkbox.addEventListener (functions.js:1022)
You will see the code and image code error. Please help. Where is the code problem?
The Document method querySelector() returns the first Element within
the document that matches the specified selector, or group of
selectors. If no matches are found, null is returned.
the return value of .querySelector() an element and not a collection of elements and therefor it is not iterable, that's why you're getting the error, use querySelectorAll instead .
replace
function checkedInputs() {
return [...querySelector('.filter_type .customCheck')].map((c) => c.checked);
}
with
function checkedInputs() {
return [...querySelectorAll('.filter_type .customCheck')].map((c) => c.checked);
}

How to get the highest level parent of clicked element [duplicate]

HTML
<body>
<div class="lol">
<a class="rightArrow" href="javascriptVoid:(0);" title"Next image">
</div>
</body>
Pseudo Code
$(".rightArrow").click(function() {
rightArrowParents = this.dom(); //.dom(); is the pseudo function ... it should show the whole
alert(rightArrowParents);
});
Alert message would be:
body div.lol a.rightArrow
How can I get this with javascript/jquery?
Here is a native JS version that returns a jQuery path. I'm also adding IDs for elements if they have them. This would give you the opportunity to do the shortest path if you see an id in the array.
var path = getDomPath(element);
console.log(path.join(' > '));
Outputs
body > section:eq(0) > div:eq(3) > section#content > section#firehose > div#firehoselist > article#firehose-46813651 > header > h2 > span#title-46813651
Here is the function.
function getDomPath(el) {
var stack = [];
while ( el.parentNode != null ) {
console.log(el.nodeName);
var sibCount = 0;
var sibIndex = 0;
for ( var i = 0; i < el.parentNode.childNodes.length; i++ ) {
var sib = el.parentNode.childNodes[i];
if ( sib.nodeName == el.nodeName ) {
if ( sib === el ) {
sibIndex = sibCount;
}
sibCount++;
}
}
if ( el.hasAttribute('id') && el.id != '' ) {
stack.unshift(el.nodeName.toLowerCase() + '#' + el.id);
} else if ( sibCount > 1 ) {
stack.unshift(el.nodeName.toLowerCase() + ':eq(' + sibIndex + ')');
} else {
stack.unshift(el.nodeName.toLowerCase());
}
el = el.parentNode;
}
return stack.slice(1); // removes the html element
}
Using jQuery, like this (followed by a solution that doesn't use jQuery except for the event; lots fewer function calls, if that's important):
$(".rightArrow").click(function () {
const rightArrowParents = [];
$(this)
.parents()
.addBack()
.not("html")
.each(function () {
let entry = this.tagName.toLowerCase();
const className = this.className.trim();
if (className) {
entry += "." + className.replace(/ +/g, ".");
}
rightArrowParents.push(entry);
});
console.log(rightArrowParents.join(" "));
return false;
});
Live example:
$(".rightArrow").click(function () {
const rightArrowParents = [];
$(this)
.parents()
.addBack()
.not("html")
.each(function () {
let entry = this.tagName.toLowerCase();
const className = this.className.trim();
if (className) {
entry += "." + className.replace(/ +/g, ".");
}
rightArrowParents.push(entry);
});
console.log(rightArrowParents.join(" "));
return false;
});
<div class=" lol multi ">
Click here
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
(In the live examples, I've updated the class attribute on the div to be lol multi to demonstrate handling multiple classes.)
That uses parents to get the ancestors of the element that was clicked, removes the html element from that via not (since you started at body), then loops through creating entries for each parent and pushing them on an array. Then we use addBack to add the a back into the set, which also changes the order of the set to what you wanted (parents is special, it gives you the parents in the reverse of the order you wanted, but then addBack puts it back in DOM order). Then it uses Array#join to create the space-delimited string.
When creating the entry, we trim className (since leading and trailing spaces are preserved, but meaningless, in the class attribute), and then if there's anything left we replace any series of one or more spaces with a . to support elements that have more than one class (<p class='foo bar'> has className = "foo bar", so that entry ends up being p.foo.bar).
Just for completeness, this is one of those places where jQuery may be overkill, you can readily do this just by walking up the DOM:
$(".rightArrow").click(function () {
const rightArrowParents = [];
for (let elm = this; elm; elm = elm.parentNode) {
let entry = elm.tagName.toLowerCase();
if (entry === "html") {
break;
}
const className = elm.className.trim();
if (className) {
entry += "." + className.replace(/ +/g, ".");
}
rightArrowParents.push(entry);
}
rightArrowParents.reverse();
console.log(rightArrowParents.join(" "));
return false;
});
Live example:
$(".rightArrow").click(function () {
const rightArrowParents = [];
for (let elm = this; elm; elm = elm.parentNode) {
let entry = elm.tagName.toLowerCase();
if (entry === "html") {
break;
}
const className = elm.className.trim();
if (className) {
entry += "." + className.replace(/ +/g, ".");
}
rightArrowParents.push(entry);
}
rightArrowParents.reverse();
console.log(rightArrowParents.join(" "));
return false;
});
<div class=" lol multi ">
Click here
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
There we just use the standard parentNode property (or we could use parentElement) of the element repeatedly to walk up the tree until either we run out of parents or we see the html element. Then we reverse our array (since it's backward to the output you wanted), and join it, and we're good to go.
I needed a native JS version, that returns CSS standard path (not jQuery), and deals with ShadowDOM. This code is a minor update on Michael Connor's answer, just in case someone else needs it:
function getDomPath(el) {
if (!el) {
return;
}
var stack = [];
var isShadow = false;
while (el.parentNode != null) {
// console.log(el.nodeName);
var sibCount = 0;
var sibIndex = 0;
// get sibling indexes
for ( var i = 0; i < el.parentNode.childNodes.length; i++ ) {
var sib = el.parentNode.childNodes[i];
if ( sib.nodeName == el.nodeName ) {
if ( sib === el ) {
sibIndex = sibCount;
}
sibCount++;
}
}
// if ( el.hasAttribute('id') && el.id != '' ) { no id shortcuts, ids are not unique in shadowDom
// stack.unshift(el.nodeName.toLowerCase() + '#' + el.id);
// } else
var nodeName = el.nodeName.toLowerCase();
if (isShadow) {
nodeName += "::shadow";
isShadow = false;
}
if ( sibCount > 1 ) {
stack.unshift(nodeName + ':nth-of-type(' + (sibIndex + 1) + ')');
} else {
stack.unshift(nodeName);
}
el = el.parentNode;
if (el.nodeType === 11) { // for shadow dom, we
isShadow = true;
el = el.host;
}
}
stack.splice(0,1); // removes the html element
return stack.join(' > ');
}
Here is a solution for exact matching of an element.
It is important to understand that the selector (it is not a real one) that the chrome tools show do not uniquely identify an element in the DOM. (for example it will not distinguish between a list of consecutive span elements. there is no positioning/indexing info)
An adaptation from a similar (about xpath) answer
$.fn.fullSelector = function () {
var path = this.parents().addBack();
var quickCss = path.get().map(function (item) {
var self = $(item),
id = item.id ? '#' + item.id : '',
clss = item.classList.length ? item.classList.toString().split(' ').map(function (c) {
return '.' + c;
}).join('') : '',
name = item.nodeName.toLowerCase(),
index = self.siblings(name).length ? ':nth-child(' + (self.index() + 1) + ')' : '';
if (name === 'html' || name === 'body') {
return name;
}
return name + index + id + clss;
}).join(' > ');
return quickCss;
};
And you can use it like this
console.log( $('some-selector').fullSelector() );
Demo at http://jsfiddle.net/gaby/zhnr198y/
The short vanilla ES6 version I ended up using:
Returns the output I'm used to read in Chrome inspector e.g body div.container input#name
function getDomPath(el) {
let nodeName = el.nodeName.toLowerCase();
if (el === document.body) return 'body';
if (el.id) nodeName += '#' + el.id;
else if (el.classList.length)
nodeName += '.' + [...el.classList].join('.');
return getDomPath(el.parentNode) + ' ' + nodeName;
};
I moved the snippet from T.J. Crowder to a tiny jQuery Plugin. I used the jQuery version of him even if he's right that this is totally unnecessary overhead, but i only use it for debugging purpose so i don't care.
Usage:
Html
<html>
<body>
<!-- Two spans, the first will be chosen -->
<div>
<span>Nested span</span>
</div>
<span>Simple span</span>
<!-- Pre element -->
<pre>Pre</pre>
</body>
</html>
Javascript
// result (array): ["body", "div.sampleClass"]
$('span').getDomPath(false)
// result (string): body > div.sampleClass
$('span').getDomPath()
// result (array): ["body", "div#test"]
$('pre').getDomPath(false)
// result (string): body > div#test
$('pre').getDomPath()
Repository
https://bitbucket.org/tehrengruber/jquery.dom.path
I've been using Michael Connor's answer and made a few improvements to it.
Using ES6 syntax
Using nth-of-type instead of nth-child, since nth-of-type looks for children of the same type, rather than any child
Removing the html node in a cleaner way
Ignoring the nodeName of elements with an id
Only showing the path until the closest id, if any. This should make the code a bit more resilient, but I left a comment on which line to remove if you don't want this behavior
Use CSS.escape to handle special characters in IDs and node names
~
export default function getDomPath(el) {
const stack = []
while (el.parentNode !== null) {
let sibCount = 0
let sibIndex = 0
for (let i = 0; i < el.parentNode.childNodes.length; i += 1) {
const sib = el.parentNode.childNodes[i]
if (sib.nodeName === el.nodeName) {
if (sib === el) {
sibIndex = sibCount
break
}
sibCount += 1
}
}
const nodeName = CSS.escape(el.nodeName.toLowerCase())
// Ignore `html` as a parent node
if (nodeName === 'html') break
if (el.hasAttribute('id') && el.id !== '') {
stack.unshift(`#${CSS.escape(el.id)}`)
// Remove this `break` if you want the entire path
break
} else if (sibIndex > 0) {
// :nth-of-type is 1-indexed
stack.unshift(`${nodeName}:nth-of-type(${sibIndex + 1})`)
} else {
stack.unshift(nodeName)
}
el = el.parentNode
}
return stack
}
All the examples from other ответов did not work very correctly for me, I made my own, maybe my version will be more suitable for the rest
const getDomPath = element => {
let templateElement = element
, stack = []
for (;;) {
if (!!templateElement) {
let attrs = ''
for (let i = 0; i < templateElement.attributes.length; i++) {
const name = templateElement.attributes[i].name
if (name === 'class' || name === 'id') {
attrs += `[${name}="${templateElement.getAttribute(name)}"]`
}
}
stack.push(templateElement.tagName.toLowerCase() + attrs)
templateElement = templateElement.parentElement
} else {
break
}
}
return stack.reverse().slice(1).join(' > ')
}
const currentElement = document.querySelectorAll('[class="serp-item__thumb justifier__thumb"]')[7]
const path = getDomPath(currentElement)
console.log(path)
console.log(document.querySelector(path))
console.log(currentElement)
var obj = $('#show-editor-button'),
path = '';
while (typeof obj.prop('tagName') != "undefined"){
if (obj.attr('class')){
path = '.'+obj.attr('class').replace(/\s/g , ".") + path;
}
if (obj.attr('id')){
path = '#'+obj.attr('id') + path;
}
path = ' ' +obj.prop('tagName').toLowerCase() + path;
obj = obj.parent();
}
console.log(path);
hello this function solve the bug related to current element not show in the path
check this now
$j(".wrapper").click(function(event) {
selectedElement=$j(event.target);
var rightArrowParents = [];
$j(event.target).parents().not('html,body').each(function() {
var entry = this.tagName.toLowerCase();
if (this.className) {
entry += "." + this.className.replace(/ /g, '.');
}else if(this.id){
entry += "#" + this.id;
}
entry=replaceAll(entry,'..','.');
rightArrowParents.push(entry);
});
rightArrowParents.reverse();
//if(event.target.nodeName.toLowerCase()=="a" || event.target.nodeName.toLowerCase()=="h1"){
var entry = event.target.nodeName.toLowerCase();
if (event.target.className) {
entry += "." + event.target.className.replace(/ /g, '.');
}else if(event.target.id){
entry += "#" + event.target.id;
}
rightArrowParents.push(entry);
// }
where $j = jQuery Variable
also solve the issue with .. in class name
here is replace function :
function escapeRegExp(str) {
return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
}
function replaceAll(str, find, replace) {
return str.replace(new RegExp(escapeRegExp(find), 'g'), replace);
}
Thanks
$(".rightArrow")
.parents()
.map(function () {
var value = this.tagName.toLowerCase();
if (this.className) {
value += '.' + this.className.replace(' ', '.', 'g');
}
return value;
})
.get().reverse().join(", ");

Can't call function on HTML element

I'm starting to write jQuery in Vanilla JS and my selectors work but when I call my append function on the HTML element I get an "is not a function" error.
var $ = function(){
this.select = function(input) {
if (input.split("")[0] == "#") {
input = input.slice(1, input.length)
return document.getElementById(input)
}
else if (input.split("")[0] == ".") {
input = input.slice(1, input.length)
return document.getElementsByClassName(input)
}
else {
return document.getElementsByTagName(input)
}
},
this.append = function(text) {
return this.innerhtml = this.innerhtml + text
}
};
my console attempts:
var myQuery = new $();
returns undefined
myQuery.select("#testspan")
returns my span tag here
myQuery.select("#testspan").append("hellohello")
returns error
VM2207:1 Uncaught TypeError: myQuery.select(...).append is not a function(…)
From your snippet the return of each of the select method return a DOM element (or collection). Really what you would like to do is called Chaining where the result of the method returns the original object. Therefore you can keep calling additional methods on the same object.
Now in your example you are going to need a collection of elements (nodes) somewhere the object can then access again. Here is a simple example.
var $ = function () {
this.nodes = [];
this.select = function (input) {
var self = this;
if (input.split("")[0] == "#") {
input = input.slice(1, input.length)
var node = document.getElementById(input);
if (node)
this.nodes.push(node);
}
else if (input.split("")[0] == ".") {
input = input.slice(1, input.length)
Array.prototype.slice.call(document.getElementsByClassName(input), 0).forEach(function (node) {
self.nodes.push(node);
});
}
else {
Array.prototype.slice.call(document.getElementsByTagName(input), 0).forEach(function (node) {
self.nodes.push(node);
});
}
return this;
},
this.append = function (text) {
this.nodes.forEach(function (i) {
i.innerHTML += text;
});
return this;
}
};
Sample Html:
<p id="test">This is test </p>
<p>This is number to</p>
Console (Chrome):
$ = new $()
$ {nodes: Array[0]}
$.select('p').append('hi')
Now a little issue here is you are (in the console) setting $ = new $() which effectivly overwrites the ability to call new $() again in the same script. I have provided a fiddle below that renames this to myQuery. Also changed that every time you call select will clear the node array.
Revised:
var myQuery = function () {
this.nodes = [];
this.select = function (input) {
this.nodes = [];
var self = this;
if (input.split("")[0] == "#") {
input = input.slice(1, input.length)
var node = document.getElementById(input);
if (node)
this.nodes.push(node);
}
else if (input.split("")[0] == ".") {
input = input.slice(1, input.length)
Array.prototype.slice.call(document.getElementsByClassName(input), 0).forEach(function (node) {
self.nodes.push(node);
});
}
else {
Array.prototype.slice.call(document.getElementsByTagName(input), 0).forEach(function (node) {
self.nodes.push(node);
});
}
return this;
},
this.append = function (text) {
this.nodes.forEach(function (i) {
i.innerHTML += text;
});
return this;
}
};
$ = new myQuery();
$.select('p').append(' test selection by tag name ');
$ = new myQuery();
$.select('.p1').append(' test selection by class ');
$ = new myQuery();
$.select('#p1').append(' test selection by id ');
$ = new myQuery();
$.select('#p2').append(' test selection by id ').append('and then chanined').select('.p2').append(' still chaining');
Fiddle: https://jsfiddle.net/kxwt9gmg/
You need to change up your approach a bit. You are wanting to store a result and call a method on it. You can ONLY call a method that that particular object has. That object you are returning, the raw html element, doesn't have that method. What you want to do is store the html element and then return an OBJECT that performs operations on what was stored. You can accomplish this using closure. For example:
function miniQuery(input){
function elementIterate(collection, action){
for (var i = elements.length -1; i >= 0; i-- ){
collection[i].style.display = action;
}
}
var isCollection = function(element){
if(element instanceof HTMLCollection){
return true
} else{
return false
}
}
function findElement(element){
if (element.startsWith("#")) {
// id element selector
return document.getElementById(element.substring(1));
} else if (element.startsWith(".")) {
// class element selector
return document.getElementsByClassName(element.substring(1));
} else {
// tag element selector
return document.getElementsByTagName(element);
};
}
if (input != undefined) {
var _this = this;
this.element = findElement(input);
var elements = findElement(input);
}
return {
append: function(content, position = 'beforeend'){
var elements = _this.element;
if (isCollection(elements)) {
for(var i = elements.length -1; i >= 0; i--){
elements[i].insertAdjacentHTML(position, content)
}
}else{
elements.insertAdjacentHTML(position, content);
}
}
}
}
function $(input){
return selector(input);
}
function selector(input){
var query = new miniQuery(input);
return query;
}

toggle class with pure javascript [duplicate]

I'm looking for a way to convert this jQuery code (which is used in responsive menu section) to pure JavaScript.
If it's hard to implement it's OK to use other JavaScript frameworks.
$('.btn-navbar').click(function()
{
$('.container-fluid:first').toggleClass('menu-hidden');
$('#menu').toggleClass('hidden-phone');
if (typeof masonryGallery != 'undefined')
masonryGallery();
});
2014 answer: classList.toggle() is the standard and supported by most browsers.
Older browsers can use use classlist.js for classList.toggle():
var menu = document.querySelector('.menu') // Using a class instead, see note below.
menu.classList.toggle('hidden-phone');
As an aside, you shouldn't be using IDs (they leak globals into the JS window object).
Here is solution implemented with ES6
const toggleClass = (el, className) => el.classList.toggle(className);
usage example
toggleClass(document.querySelector('div.active'), 'active'); // The div container will not have the 'active' class anymore
Take a look at this example: JS Fiddle
function toggleClass(element, className){
if (!element || !className){
return;
}
var classString = element.className, nameIndex = classString.indexOf(className);
if (nameIndex == -1) {
classString += ' ' + className;
}
else {
classString = classString.substr(0, nameIndex) + classString.substr(nameIndex+className.length);
}
element.className = classString;
}
don't need regex just use classlist
var id=document.getElementById('myButton');
function toggle(el,classname){
if(el.classList.contains(classname)){
el.classList.remove(classname)
}
else{
el.classList.add(classname)
}
}
id.addEventListener('click',(e)=>{
toggle(e.target,'red')
})
.red{
background:red
}
<button id="myButton">Switch</button>
Simple Usage above Example
var id=document.getElementById('myButton');
function toggle(el,classname){
el.classList.toggle(classname)
}
id.addEventListener('click',(e)=>{
toggle(e.target,'red')
})
.red{
background:red
}
<button id="myButton">Switch</button>
This one works in earlier versions of IE also.
function toogleClass(ele, class1) {
var classes = ele.className;
var regex = new RegExp('\\b' + class1 + '\\b');
var hasOne = classes.match(regex);
class1 = class1.replace(/\s+/g, '');
if (hasOne)
ele.className = classes.replace(regex, '');
else
ele.className = classes + class1;
}
.red {
background-color: red
}
div {
width: 100px;
height: 100px;
margin-bottom: 10px;
border: 1px solid black;
}
<div class="does red redAnother " onclick="toogleClass(this, 'red')"></div>
<div class="does collapse navbar-collapse " onclick="toogleClass(this, 'red')"></div>
This is perhaps more succinct:
function toggle(element, klass) {
var classes = element.className.match(/\S+/g) || [],
index = classes.indexOf(klass);
index >= 0 ? classes.splice(index, 1) : classes.push(klass);
element.className = classes.join(' ');
}
If anyone looking to toggle class on mousehover/mousleave using Javascript here is the code for it
function changeColor() {
this.classList.toggle('red');
this.classList.toggle('green');
}
document.querySelector('#btn').addEventListener('mouseenter', changeColor);
document.querySelector('#btn').addEventListener('mouseleave', changeColor );
Demo Fiddle link: https://jsfiddle.net/eg2k7mLj/1/
Source: Toggle Class (Javascript based, without jQuery)
Just for legacy reasons:
function toggleClassName(elementClassName, className) {
const originalClassNames = elementClassName.split(/\s+/);
const newClassNames = [];
let found = false;
for (let index = 0; index < originalClassNames.length; index++) {
if (originalClassNames[index] === '') {
continue;
}
if (originalClassNames[index] === className) {
found = true;
continue;
}
newClassNames.push(originalClassNames[index]);
}
if (!found) {
newClassNames.push(className);
}
return newClassNames.join(' ');
}
console.assert(toggleClassName('', 'foo') === 'foo');
console.assert(toggleClassName('foo', 'bar') === 'foo bar');
console.assert(toggleClassName('foo bar', 'bar') === 'foo');
console.assert(toggleClassName('bar foo', 'bar') === 'foo');
console.assert(toggleClassName('foo bar baz', 'bar') === 'foo baz');
console.assert(toggleClassName('foo-bar', 'foo') === 'foo-bar foo');
console.assert(toggleClassName('bar foo-bar', 'bar') === 'foo-bar');
console.assert(toggleClassName('bar bar bar foo-bar bar', 'bar') === 'foo-bar');
console.assert(toggleClassName(" \n\nbar-bar \nbar-baz foo", 'bar-baz') === 'bar-bar foo');
element.className = toggleClassName(element.className, 'foo');
Try this (hopefully it will work):
// mixin (functionality) for toggle class
function hasClass(ele, clsName) {
var el = ele.className;
el = el.split(' ');
if(el.indexOf(clsName) > -1){
var cIndex = el.indexOf(clsName);
el.splice(cIndex, 1);
ele.className = " ";
el.forEach(function(item, index){
ele.className += " " + item;
})
}
else {
el.push(clsName);
ele.className = " ";
el.forEach(function(item, index){
ele.className += " " + item;
})
}
}
// get all DOM element that we need for interactivity.
var btnNavbar = document.getElementsByClassName('btn-navbar')[0];
var containerFluid = document.querySelector('.container-fluid:first');
var menu = document.getElementById('menu');
// on button click job
btnNavbar.addEventListener('click', function(){
hasClass(containerFluid, 'menu-hidden');
hasClass(menu, 'hidden-phone');
})`enter code here`
Here is a code for IE >= 9 by using split(" ") on the className :
function toggleClass(element, className) {
var arrayClass = element.className.split(" ");
var index = arrayClass.indexOf(className);
if (index === -1) {
if (element.className !== "") {
element.className += ' '
}
element.className += className;
} else {
arrayClass.splice(index, 1);
element.className = "";
for (var i = 0; i < arrayClass.length; i++) {
element.className += arrayClass[i];
if (i < arrayClass.length - 1) {
element.className += " ";
}
}
}
}
If you want to toggle a class to an element using native solution, you could try this suggestion. I have tasted it in different cases, with or without other classes onto the element, and I think it works pretty much:
(function(objSelector, objClass){
document.querySelectorAll(objSelector).forEach(function(o){
o.addEventListener('click', function(e){
var $this = e.target,
klass = $this.className,
findClass = new RegExp('\\b\\s*' + objClass + '\\S*\\s?', 'g');
if( !findClass.test( $this.className ) )
if( klass )
$this.className = klass + ' ' + objClass;
else
$this.setAttribute('class', objClass);
else
{
klass = klass.replace( findClass, '' );
if(klass) $this.className = klass;
else $this.removeAttribute('class');
}
});
});
})('.yourElemetnSelector', 'yourClass');
I know that I am late but, I happen to see this and I have a suggestion..
For those looking for cross-browser support, I wouldn't recommend class toggling via JS.
It may be a little more work but it is more supported through all browsers.
document.getElementById("myButton").addEventListener('click', themeswitch);
function themeswitch() {
const Body = document.body
if (Body.style.backgroundColor === 'white') {
Body.style.backgroundColor = 'black';
} else {
Body.style.backgroundColor = 'white';
}
}
body {
background: white;
}
<button id="myButton">Switch</button>
function navbarToggler() {
const collapseBtn = document.querySelector('.collapseBtn').classList
collapseBtn.toggle('collapse')
}

Highlight Text in textarea when a Hashtag typed followed by one or more character

I want to be able to let the user wile typing in the text area if he typed a hash-tag followed by one or more character as he type the text get highlighted till he hits space.
The thing is i want to achieve something like Facebook's new hash-tag feature, I've done the logic the coding but still not able to achieve it visually.
The approach i tried is by using Jquery as follows:
<textarea id="txtArea">Here is my #Hash</textarea>
$("#txtArea").onkeyup(function(){
var result = $("#txtArea").match(/ #[\w]+/g);
//result = '#Hash'
});
But i couldn't complete & i don't know where to go from here so any solution, advice, plugin that i could use i'll be very grateful.
I don't believe there is any way to highlight words (other than one single highlight) within a basic textarea as it does not accept markup, you could turn the textarea into a small Rich Text editor, but that seems a little overcomplicated. I would probably go for similar approach to the editor here on SO, and have a preview window below so that you can see what is being marked. You could use something like this, of course you may want to change how it works a little to suit your exact needs. It should at least give you some ideas.
CSS
#preview {
height: 2em;
width: 12em;
border-style: solid;
border-width: 1px;
}
.hashSymbol {
color: #f90;
}
HTML
<textarea id="userInput"></textarea>
<div id="preview"></div>
Javascript
/*jslint maxerr: 50, indent: 4, browser: true */
(function () {
"use strict";
function walkTheDOM(node, func) {
func(node);
node = node.firstChild;
while (node) {
walkTheDOM(node, func);
node = node.nextSibling;
}
}
function getTextNodes(element) {
var nodes = [];
walkTheDOM(element, function (node) {
if (node.nodeType === 3) {
nodes.push(node);
}
});
return nodes;
}
function escapeRegex(string) {
return string.replace(/[\[\](){}?*+\^$\\.|]/g, "\\$&");
}
function highlight(element, string, classname) {
var nodes = getTextNodes(element),
length = nodes.length,
stringLength = string.length,
rx = new RegExp("\\B" + escapeRegex(string)),
i = 0,
index,
text,
newContent,
span,
node;
while (i < length) {
node = nodes[i];
newContent = document.createDocumentFragment();
text = node.nodeValue;
index = text.search(rx);
while (index !== -1) {
newContent.appendChild(document.createTextNode(text.slice(0, index)));
text = text.slice(index + stringLength);
span = document.createElement("span");
span.className = classname;
span.appendChild(document.createTextNode(string));
newContent.appendChild(span);
index = text.search(rx);
}
newContent.appendChild(document.createTextNode(text));
node.parentNode.replaceChild(newContent, node);
i += 1;
}
}
function addEvent(elem, event, fn) {
if (typeof elem === "string") {
elem = document.getElementById(elem);
}
function listenHandler(e) {
var ret = fn.apply(null, arguments);
if (ret === false) {
e.stopPropagation();
e.preventDefault();
}
return ret;
}
function attachHandler() {
window.event.target = window.event.srcElement;
var ret = fn.call(elem, window.event);
if (ret === false) {
window.event.returnValue = false;
window.event.cancelBubble = true;
}
return ret;
}
if (elem.addEventListener) {
elem.addEventListener(event, listenHandler, false);
} else {
elem.attachEvent("on" + event, attachHandler);
}
}
function emptyNode(node) {
while (node.firstChild) {
node.removeChild(node.firstChild);
}
}
function toPreviewHighlight(e, to) {
if (typeof to === "string") {
to = document.getElementById(to);
}
var value = e.target.value,
tags = value.match(/\B#\w+/g) || [],
index = tags.length - 1,
lookup = {},
fragment,
length,
tag;
while (index >= 0) {
tag = tags[index];
if (!tag.length || tag === "#" || tag.charAt(0) !== "#" || lookup[tag]) {
tags.splice(index, 1);
} else {
lookup[tag] = true;
}
index -= 1;
}
fragment = document.createDocumentFragment();
fragment.appendChild(document.createTextNode(value));
index = 0;
length = tags.length;
while (index < length) {
tag = tags[index];
highlight(fragment, tag, "hashSymbol");
index += 1;
}
emptyNode(to);
to.appendChild(fragment);
}
addEvent("userInput", "keyup", function (e) {
toPreviewHighlight(e, "preview");
});
}());
On jsfiddle
This code is slightly modified from other questions and answers here on SO (resusing code is good)
if text contains '#' change color of '#'
How to extract value between # and space in textarea value
Using JavaScript, I want to use XPath to look for the presence of a string, then refresh the page based on that

Categories

Resources