How to replace a className on vanilla JS? - javascript

How can i replace a className (List) into Javascript? I have this code:
document.addEventListener("DOMContentLoaded", (e) => {
const url = window.location;
let urlReq = url.href;
if(urlReq.includes('/home')){
document.getElementById('default-sidenav-menu').className.replace('sidenav-main nav-expanded nav-lock nav-collapsible sidenav-active-rounded sidenav-dark', 'sidenav-main nav-collapsed nav-collapsible sidenav-active-rounded sidenav-dark');
console.log(document.getElementById('default-sidenav-menu').className);
}
});
this is my html:
<aside class="sidenav-main nav-expanded nav-lock nav-collapsible sidenav-active-rounded sidenav-dark" id="default-sidenav-menu">
<div class="brand-sidebar">
...
</aside>
but i am still getting the default className: sidenav-main nav-expanded nav-lock nav-collapsible sidenav-active-rounded sidenav-dark when my page was loaded.
What error i made?

You do not need to replace, simply assign the string classes to the className property:
The className property of the Element interface gets and sets the value of the class attribute of the specified element.
Syntax:
elementNodeReference.className = cName;
Where:
cName is a string variable representing the class or space-separated classes of the current element.
Demo:
document.addEventListener("DOMContentLoaded", (e) => {
// other code
document.getElementById('default-sidenav-menu').className = 'sidenav-main nav-expanded nav-lock nav-collapsible sidenav-active-rounded sidenav-dark', 'sidenav-main nav-collapsed nav-collapsible sidenav-active-rounded sidenav-dark';
console.log(document.getElementById('default-sidenav-menu').className);
// other code
});
<aside class="nav-lock nav-collapsible" id="default-sidenav-menu">
<div class="brand-sidebar">
</div>
</aside>

document.getElementById('default-sidenav-menu').className.replace
This means that you are replacing the string with another string because "className" is just a string when you get it.
document.body.className.constructor.name
=> "String"
If you want to replace it, then you should assign the new class name like this.
document.getElementById('default-sidenav-menu').className =
document.getElementById('default-sidenav-menu').className.replace("something", "something_new");

Related

How do I make a conditional with hasClass that prints a text depending on the content of another div tag?

For example if have:
<div>New York</div>
So I would like to print another div like this:
<div>Free Delivery</div>
How would I do that using JS?
you can do this with simple js condtion:
add a class/id to to the element <div id="country">New York</div>
and in your script:
window.onload = () => {
const country = document.querySelector('#country');
const printdiv = '<div>Free Delivery</div>';
if(country && country.textContent.trim() == 'New York'){
document.querySelector('#country').insertAdjacentHTML('afterend',printdiv)
}
}
add id to element :
<div id="country">New York</div>
and then
let country = $('#country')
setInterval(()=>{
if(country.text()=='New York'){
// Your code
// exemple
alert('Congratulations ! We Ship for Free for New York
Customers');
}
},3000)
i hope it was useful

Looping through all divs inside a container div using jQuery

I'm trying to do a sort of invoicing system, and the html looks like this:
<invoice>
<headers>
<div date contenteditable>15-Jan-2020</div>
<div buyer contenteditable>McDonalds</div>
<div order contenteditable>145632</div>
</headers>
<item>
<div name contenteditable>Big Mac</div>
<div quantity contenteditable>5</div>
<div rate contenteditable>20.00</div>
</item>
<item>
<div name contenteditable>Small Mac</div>
<div quantity contenteditable>10</div>
<div rate contenteditable>10.00</div>
</item>
</invoice>
<button>Loop</button>
I need to loop through each <invoice> and get details from <headers> and <item>, so the end results look like this.
date : 15-Jan-2020 buyer : McDonalds order:145632
item : Big Mac quantity : 5 rate : 20.00
item : Small Mac quantity : 10 rate : 10.00
I plan on sending this data as json to a PHP script for processing.
The problem is, <headers>,<items> wont be the only containers in each invoice. There could be <address>,<transporter> etc. but they'll all be inside each <invoice>.
With that being the case, how can I loop through each container and get it's data?
Here's the jQuery I was attempting:
var button = $("button")
button.on("click", function() {
$('invoice').each(function() {
alert('It works');
});
});
Fiddle here
You can loop through div and use data-attribute for name label as below
$('invoice>headers>div, invoice>item>div').each(function(index,item) {
console.log($(this).attr('data-name'), $(this).text());
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<invoice>
<headers>
<div date contenteditable data-name="date">15-Jan-2020</div>
<div buyer contenteditable data-name="buyer">McDonalds</div>
<div order contenteditable data-name="order">145632</div>
</headers>
<item>
<div name contenteditable data-name="name">Big Mac</div>
<div quantity contenteditable data-name="quantity">5</div>
<div rate contenteditable data-name="rate">20.00</div>
</item>
<item>
<div name contenteditable data-name="name">Small Mac</div>
<div quantity contenteditable data-name="quantity">10</div>
<div rate contenteditable data-name="rate">10.00</div>
</item>
</invoice>
$('headers > div, item > div').each(function(item) {
console.log('item');
});
It seems your HTML isn't valid HTML. The spec doesn't define elements like <invoice>, <headers> and <item>. Besides that, attributes on elements almost always resemble key-value pairs, meaning you should declare your name, buyer, order, quantity and rate attributes as values of existing attributes. The contenteditable attribute is a boolean attribute which is OK to be left as it currently is.
Here is a fixed and working example:
var button = $('#read-invoice');
// readLine :: [String] -> (HTMLElement -> String)
function readLine(fields) {
return function (el) {
return fields.reduce(function (txt, field) {
var data = $('.' + field, el).text();
return txt === ''
? field + ': ' + data
: txt + '; ' + field + ': ' + data
}, '');
}
}
// readBlock :: { (HTMLElement -> String) } -> (HTMLElement -> String)
function readBlock(readers) {
return function (el) {
var rtype = el.className;
if (typeof readers[rtype] === 'function') {
return readers[rtype](el);
}
return '';
}
}
// autoRead :: HTMLElement -> String
var autoRead = readBlock({
headers: readLine(['date', 'buyer', 'order']),
item: readLine(['name', 'quantity', 'rate'])
// ... address, etc.
});
button.on('click', function () {
var result = $('.invoice').
children().
toArray().
reduce(function (txt, el) {
var line = autoRead(el);
return line === ''
? txt
: txt + line + '\n';
}, '');
console.log(result);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="invoice">
<div class="headers">
<div class="date" contenteditable>15-Jan-2020</div>
<div class="buyer" contenteditable>McDonalds</div>
<div class="order" contenteditable>145632</div>
</div>
<div class="item">
<div class="name" contenteditable>Big Mac</div>
<div class="quantity" contenteditable>5</div>
<div class="rate" contenteditable>20.00</div>
</div>
<div class="item">
<div class="name" contenteditable>Small Mac</div>
<div class="quantity" contenteditable>10</div>
<div class="rate" contenteditable>10.00</div>
</div>
</div>
<button id="read-invoice">Loop</button>
JS explanation
The function readLine takes an Array of Strings, where each String resembles the class name of one of the inner <div> elements. It returns a function that's waiting for a "block" element (like <div class="headers">) and reads the contents of it's contained <div>'s into a single String. Let's call the returned function a reader.
The readBlock function takes an Object of reader functions and returns a function taking a "block" element. The returned function determines which type of "block" it received and calls the matching reader function with the element as argument. If no reader matches the block type, it returns the empty String.
In the end, autoRead becomes a single function taking in a whole "block" element and returning all of it's contents as a line of text.
The button click handler looks up the <div class="invoice"> element, traverses it's DOM tree down to it's child elements (our "block" elements) and passes each "block" to autoRead, building up a result String. The final result is logged to the console.
Extending
To add new types of "block"s, simply define a new reader for it and add it to the Object passed to readBlock. For example, to add an <div class="address"> reader that reads "name", "street", "zip" and "city" infos:
var autoRead = readBlock({
headers: readLine(['date', 'buyer', 'order']),
item: readLine(['name', 'quantity', 'rate']),
address: readLine(['name', 'street', 'zip', 'city']) // <<< new
});
Extending the fields a certain reader reads is also simple, just add the name of the field to read:
var autoRead = readBlock({
headers: readLine(['date', 'buyer', 'order']),
item: readLine(['name', 'quantity', 'rate', 'currency']) // <<< added "currency"
});

How to select a class name knowing only a part of name in Javascript

In Javascript with editor.selection.getContent() function i get the image selected in the wp editor:
<img class="alignnone wp-image-xxx size-large" src="http://....." alt="" />
i need to select the class wp-image-xxx and replace it with a class that include a new id (xxx).
With classList i can get all classes:
var imgselected = editor.selection.getNode();
var classes = imgselected.classList;
But how can i select in javascript the class wp-image-xxx without knowing the xxx value so to be able after to replace it?
Here is the most basic way that is applicable in your case:
//Replaces the first class that matches the name given as its "start" of string with a new class name;
function replaceClass(element, searchClassName, replacementClassName) {
element.classList.remove(findClassName(document.getElementById('foo'), searchClassName));
element.classList.add(replacementClassName)
//used to find a className
function findClassName(element, searchPhrase) {
var classNameFound = "";
element.classList.forEach(function(className) {
if (className.substring(0, searchPhrase.length) === searchPhrase) {
//Element found
if (!classNameFound)
classNameFound = className;
}
});
return classNameFound;
}
}
just run it with something like:
replaceClass(document.getElementById('foo'), 'wp-image', 'newClassName');

Highlighting when HTML and Xpath is given

Given the HTML as a string, the Xpath and offsets. I need to highlight the word.
In the below case I need to highlight Child 1
HTML text:
<html>
<body>
<h2>Children</h2>Joe has three kids:<br/>
<ul>
<li>
Child 1 name
</li>
<li>kid2</li>
<li>kid3</li>
</ul>
</body>
</html>
XPATH as : /html/body/ul/li[1]/a[1]
Offsets: 0,7
Render - I am using react in my app.
The below is what I have done so far.
public render(){
let htmlText = //The string above
let doc = new DOMParser().parseFromString(htmlRender,'text/html');
let ele = doc.evaluate("/html/body/ul/li[1]/a[1]", doc, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null); //This gives the node itself
let spanNode = document.createElement("span");
spanNode.className = "highlight";
spanNode.appendChild(ele);
// Wrapping the above node in a span class will add the highlights to that div
//At this point I don't know how to append this span to the HTML String
return(
<h5> Display html data </h5>
<div dangerouslySetInnerHTML={{__html: htmlText}} />
)
I want to avoid using jquery. Want to do in Javascript(React too) if possible!
Edit:
So if you notice the Render function it is using dangerouslySetHTML.
My problem is I am not able manipulate that string which is rendered.
This is what I ended up doing.
public render(){
let htmlText = //The string above
let doc = new DOMParser().parseFromString(htmlRender,'text/html');
let xpathNode = doc.evaluate("/html/body/ul/li[1]/a[1]", doc, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
const highlightedNode = xpathNode.singleNodeValue.innerText;
const textValuePrev = highlightedNode.slice(0, char_start);
const textValueAfter = highlightedNode.slice(char_end, highlightedNode.length);
xpathNode.singleNodeValue.innerHTML = `${textValuePrev}
<span class='pt-tag'>
${highlightedNode.slice(char_start, char_end)}
</span> ${textValueAfter}`;
return(
<h5> Display html data </h5>
<div dangerouslySetInnerHTML={{__html: doc.body.outerHTML}} />
)
Xpath is inherently cross component, and React components shouldn't know much about each other. Xpath also basically requires all of the DOM to be created in order to query it. I would render your component first, then simply mutate the rendered output in the DOM using the Xpath selector.
https://jsfiddle.net/69z2wepo/73860/
var HighlightXpath = React.createClass({
componentDidMount() {
let el = document.evaluate(this.props.xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
el.singleNodeValue.style.background = 'pink';
},
render: function() {
return this.props.children;
}
});
Usage:
<HighlightXpath xpath="html//body//div/p/span">
... app ...
</HighlightXpath>

How to find an element by data attribute with find() function?

I add a data attribute to an element via jquery data() function.
I want to use find() function to get the element. But obviously, it does not work.
What I want to do is caching the element's parent element and do a lot of things.
Like this:
var $parent = $('#parent');
var $dataElement = $parent.findByData('whatever');
$parent.xxx().xxx().xxx()....;
I don't want this:
var $parent = $('#parent');
var $dataElement = $("#parent [data-whatever='whatever']");
$parent.xxx().xxx().xxx()....;
//It looks like find the parent twice.
Can any function do this?
I add a data attribute to an element via jquery data() function.
As you mentioned you are setting the data to the element with data() method of jQuery. Which doesn't adds any attribute in the DOM. So you can't find it with .find() that way because it's in memory*.
Instead you should use .attr() method to set the data attribute and then you can read it from the DOM with .find() method.
* don't have proper word for it
below is an example of setting the data with .data() and trying to find it.
$('#parent').find('.two').data('test', 'myTest');
var div = $('#parent').find('.child[data-test="myTest"]').length;
alert(div);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id='parent'>
<div class='child one'></div>
<div class='child two'></div>
</div>
below is an example of setting the data with .attr() and trying to find it.
$('#parent').find('.two').attr('data-test', 'myTest');
var div = $('#parent').find('.child[data-test="myTest"]').length;
alert(div);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id='parent'>
<div class='child one'></div>
<div class='child two'></div>
</div>
below is an example as per your comment:
$('#parent').find('.two').data('test', 'myTest');
var div = $('#parent').find('.child').filter(function(){
return $(this).data('test') == 'myTest'
}).text();
console.log(div);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id='parent'>
<div class='child one'>One</div>
<div class='child two'>Two</div>
</div>
You can try $(child,parent) way and attribute selector $('[attribute-name]') as parameter,
var $parent = $('#parent');
var $dataElement = $parent.children().filter(function(){
return $(this).data('whatever') !== undefined
});
If you need a function findByData(),
$.fn.findByData = function(dataAttribute){
return $(this).children().filter(function(){
return $(this).data(dataAttribute) !== undefined
});
}
var $parent = $('#parent')
var $dataElement = $parent.findByData('whatever');
Fiddle Demo
If you want to get the parent element only when it has data attribute value equals to somevalue, you need to use filter function:
$parent.filter(function(){
return $(this).data('whatever') == "whatever"
});
If you want to find child element of parent that has data attribute value equals to somevalue:
$parent.find("*").filter(function(){
return $(this).data('whatever') == "whatever"
});;
<div id="parent">
<p data-whatever='whatever'>Whatever1</p>
<p data-whatever='whatever'>Whatever2</p>
<p data-whatever='whereever'>Whereever1</p>
</div>
var $parent = $('#parent');
var $dataElements = $parent.find("*[data-whatever='whatever']");
This will return an array of decedent elements inside the "#parent" element having data-whatever='whatever'.
$.each($dataElements,function(key,val){
console.log( $($dataElements[key]).html());
});
Demo

Categories