I have the following HTML code and I need to console.log only Shipping.
I tried a few methods but can't seem to get it to work.
I tried selecting first its children and printing out the textContent of its parent - no go. I could delete its children and print out what's left but I can't do that.
Any suggestions?
<div class="accordion shadowed-box shipping collapsed summary">
<fieldset>
<legend>
Shipping
<div id="shippingTooltip" class="form-field-tooltip cvnship-tip" role="tooltip">
<span class="tooltip">
<div class="tooltip-content" data-layout="small tooltip-cvn">
<div id="cart-checkout-shipping-tooltip" class="html-slot-container">
<p>We ship UPS, FedEx and/or USPS Priority Mail.<br>
<a class="dialogify" data-dlg-options="{"height":200}" href="https://www.payless.com/customer-service/ordering-and-shipping/cs-ordering-shipping-schedule.html" title="shipping information">Learn more about our shipping methods and prices.</a>
</p>
</div>
</div>
</span>
</div>
Edit
</legend>
</fieldset>
</div>
I tried this:
var accordionChildren = document.querySelector('.accordion.shadowed-box.shipping>fieldset>legend *');//selects the first child
var accordionTitle = accordionChildren.parentElement;
var text = accordionTitle.textContent;
console.log(text);
I want to get Shipping but instead I get still all the text contents of the legend element.
you can access Text nodes by iterating over the child nodes (or access the intended node directly) of the accordionTitle variable.
let textNode = accordionTitle.childNodes[0],
text = textNode.textContent;
console.log(text);
See https://developer.mozilla.org/en-US/docs/Web/API/Node/childNodes and https://developer.mozilla.org/en-US/docs/Web/API/Text
You just need to find the TextNode child from all of the elements children, you do this by iterating over all of the childNodes and when the node type matches TextNode, return its textContext.
For a jQuery based solution on how to pick the TextNode child of an element see this question - but my example shows how to do it in vanilla ES (with a for loop over childNodes):
Object.defineProperty(HTMLElement.prototype, 'onlyTextContent', {
enumerable: false,
configurable: false,
get: function() {
for(let i = 0; i < this.childNodes.length; i++) {
if(this.childNodes[i].nodeType === Node.TEXT_NODE) {
return this.childNodes[i].textContent.trim();
}
}
return null;
}
});
document.addEventListener('DOMContentLoaded', function() {
console.log(
document.getElementById('legend1').onlyTextContent
);
});
<div class="accordion shadowed-box shipping collapsed summary">
<fieldset>
<legend id="legend1">
Shipping
<div id="shippingTooltip" class="form-field-tooltip cvnship-tip" role="tooltip">
<span class="tooltip">
<div class="tooltip-content" data-layout="small tooltip-cvn">
<div id="cart-checkout-shipping-tooltip" class="html-slot-container">
<p>We ship UPS, FedEx and/or USPS Priority Mail.<br>
<a class="dialogify" data-dlg-options="{"height":200}" href="https://www.payless.com/customer-service/ordering-and-shipping/cs-ordering-shipping-schedule.html" title="shipping information">Learn more about our shipping methods and prices.</a>
</p>
</div>
</div>
</span>
</div>
Edit
</legend>
</fieldset>
</div>
You can get the contents of the <legend> tag as a string and then use a regular expression to remove the HTML tags and their content inside. Like this:
let legends = document.querySelector('.accordion.shadowed-box.shipping>fieldset>legend');
let title = legends.innerHTML.replace(/<.*/s, '');
// title = "Shipping"
The regular expression matches the first < character and everything that follows. So we replace that match with an empty string ''.
Related
I'm trying to clean up the results presented on my HTML file with Jquery. I want to keep removing words that are repeated more than one time.
A quick example
Accents Australian
Accents English (RP)
Dance Hip Hop
Dance Jazz
It should be output as
Accents
Australian
English (RP)
Dance
Hip Hop
Jazz
My original HTML looks like this
<div role="list" class="skill-items">
<div role="listitem" class="skill-item">
<div class="skill-category">Accents</div>
<div>Australian</div>
</div>
<div role="listitem" class="skill-item">
<div class="skill-category">Accents</div>
<div>English (RP)</div>
</div>
<div role="listitem" class="skill-item">
<div class="skill-category">Dance</div>
<div>Hip Hop</div>
</div>
<div role="listitem" class="skill-item">
<div class="skill-category">Dance</div>
<div>Jaz</div>
</div>
</div>
I tried my best but I'm not landing in a good place
$('.skill-category').text(function(index, oldText) {
return oldText.replace($(this).parent().next().find('.skill-category').text(), '');
})
Any suggestion?
Please check below working code:
const category = [...document.querySelectorAll('.skill-item > .skill-category')];
const texts = new Set(category.map(x => x.innerHTML));
category.forEach(category => {
if(texts.has(category.innerHTML)){
texts.delete(category.innerHTML);
}
else{
category.remove()
}
})
As per you question and shared HTML above is the working code for the same and if you add more similar things it will help.
Please let me know if you find any issues
Your question can be broken into two problems:
You want to group the elements with the same value for .skill-category
You want to change <div> elements into a list.
Grouping the elements could by done like so:
For every category, take a look at the previous element.
Does it contain the same category? If not, then continue to the next category.
If so, take everything after .skill-category (in your example HTML, that's a single <div>. Cut-and-paste it at the end of the aforementioned previous element.
For the second problem:
Changing an element (<div> to <li>) is not possible. You can create a new <li> and move what's inside the <div> into it. Of course, you'll need a <ul> that wraps the <li>s as well.
Take the .skill-category elements
Find all the content that follows the category (in your case, 1+ <div> elements)
Put the contents of the matched elements into a new <li>.
Put all the <li>s of a single category into a <ul>.
Remove the matched elements (in your case, the <div>(s)) since we've moved all their content to a different node. They're now empty tags and useless.
Put the <ul> after the .skill-category.
// Grouping the results.
$('.skill-category').each(function() {
// Get the previous .skill-item and find the category.
var prev = $(this).parent().prev('.skill-item').find('.skill-category');
// Check if the previous category === this category.
var same = !!(prev.length && prev.text() === $(this).text());
if (!same) {
return; // Do nothing.
}
// Take every element after the category and move it to the
// previous .skill-item.
prev.after($(this).nextAll());
// Then remove the now-empty category.
// All content has been moved to the previous element, after all.
$(this).parent().remove();
});
// Wrapping the contents of a category in a list.
$('.skill-category').each(function() {
var list = $('<ul></ul');
// Find everything after the category.
$(this).nextAll().each(function() {
// Create a <li> and move the child elements to it.
// Then add the <li> to the <ul>.
$('<li></li>').append($(this).contents()).appendTo(list);
}).remove(); // remove the now empty elements.
// Add the list to current .skill-category.
$(this).append(list);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div role="list" class="skill-items">
<div role="listitem" class="skill-item">
<div class="skill-category">Accents</div>
<div>Australian</div>
</div>
<div role="listitem" class="skill-item">
<div class="skill-category">Accents</div>
<div>English (RP)</div>
</div>
<div role="listitem" class="skill-item">
<div class="skill-category">Dance</div>
<div>Hip Hop</div>
</div>
<div role="listitem" class="skill-item">
<div class="skill-category">Dance</div>
<div>Jaz</div>
</div>
</div>
This is the issue: I want to have nested divs with paragraphs inside with different texts.
I want to be able to get the paragraph that contains certain word, for example "mate" I did the below HTML structure trying to obtain an HTML collection and iterate it, and then using javascript, try to use the includes method to get the paragraph than contains that word, and finally, try to find a way to get the full path from the uppermost div to this p.
<div class="grandpa">
<div class="parent1">
<div class="son1">
<p>I like oranges</p>
</div>
<div class="son2">
<p>yeeeey</p>
<p>wohoo it's saturday</p>
</div>
<div class="son3"></div>
</div>
<div class="parent2"></div>
<div class="parent3">
<div class="son1">
<p>your team mate has been killed!</p>
<p>I should stop playing COD</p>
</div>
<div class="son2"></div>
</div>
</div>
I actually don't know how to achieve it, but at least I wanted to get an HTML collection to iterate, but I'm not being able to get it.... When I use this:
const nodes = document.querySelector('.grandpa');
console.log(typeof nodes);
I don't get an HTML collection, instead if I console.log typeof nodes variable it says it is an object..
How can I iterate this DOM tree, capture the element that contais the word "mate", and obtain (this is what I really want to achieve) the path to it?
Thanks!
You can loop through every element, remove all children elements, then check whether the textContent includes the string you are looking for:
const allElements = document.body.querySelectorAll('*');
const lookFor = "mate";
var elem;
for (let i = 0; i < allElements.length; i++) {
const cur = allElements[i].cloneNode(true); //doesn't mess up the original element when removing children
while (cur.lastElementChild) {
cur.removeChild(cur.lastElementChild);
}
if (cur.textContent.includes(lookFor)) {
elem = cur;
break;
}
}
console.log(elem);
<div class="grandpa">
<div class="parent1">
<div class="son1">
<p>I like oranges</p>
</div>
<div class="son2">
<p>yeeeey</p>
<p>wohoo it's saturday</p>
</div>
<div class="son3"></div>
</div>
<div class="parent2"></div>
<div class="parent3">
<div class="son1">
<p>your team mate has been killed!</p>
<p>I should stop playing COD</p>
</div>
<div class="son2"></div>
</div>
</div>
I have a div that has notes within it (each note has a title and content). When creating a new note, it gets its own title and content added into this div.
<div class="notes-list">
<h4 class="note-title">Title</h4>
<input class="note-content">Note content</input>
</div>
How can I make the input hide/show when I click on the title related to it?
const titles = document.querySelectorAll(".note-title");
const noteContent = document.querySelectorAll(".note-content");
titles.forEach(function(title) {
title.addEventListener("click", () => {
// unsure how to select the corresponding input related to the title:
noteContent.style.display = "inline-block";
});
});
Your not that far off. Just a few changes necessary. Since querySelectorAll returns a nodeList you cannot access your element directly, instead we can use the index from our forEach to find the correct element.
The easiest method to toggle elements would be to use a CSS class together with classList.toggle(). Another option could be to check its display style and change the property accordingly.
As a side note: An input element is selfclosing meaning the </input> is obsolete. If you want to give it a label use as the name suggests the label element. More info on closing an input tag in this thread.
const titles = document.querySelectorAll(".note-title");
const noteContent = document.querySelectorAll(".note-content");
titles.forEach(function(title, index) {
title.addEventListener("click", () => {
// unsure how to select the corresponding input related to the title:
noteContent[index].classList.toggle("hide");
});
});
.hide {
display: none;
}
<div class="notes-list">
<h4 class="note-title">Title 1</h4>
<label>
<input class="note-content">
Note content 1
</label>
<h4 class="note-title">Title 2</h4>
<label>
<input class="note-content">
Note content 2
</label>
<h4 class="note-title">Title 3</h4>
<label>
<input class="note-content">
Note content 3
</label>
</div>
How can you replace HTML tag with all tags branching inside using Javascript with other HTML code?
example:
<div class="a">
<div class="sub-a1">
<div class="sub-a12">
</div>
</div>
<div class="sub-a2">
<div class="sub-b">
</div>
</div>
I wanna replace all tags from tag div class 'a' including all sub nodes with another code.
is that's possible?
please help me.
const target = document.querySelector(".a");
target.innerHTML = //place your html here as string
Yes, this is possible. If you want to keep the div.a elements and just change the "subnodes" you have to use innerHTML in stead of outerHTML.
const divs = [...document.getElementsByClassName("a")]; //make a copy of the HTML collection so that they can be removed without being removed in the array
const newElement = "<h1>Replaced Element</h1>"; //this is your replacement element
for (let i = 0; i < divs.length; i++) { // loop through all the divs
divs[i].outerHTML = newElement; // set the outer html for the div to the replacement elemzent
}
You can do with .replaceWith() with a valid HTML code.
function replace() {
var para = document.createElement("P"); // Create a <p> element
para.innerText = "This is a paragraph"; // Insert text
document.querySelector(".a").replaceWith(para);
}
<div class="a">
<div class="sub-a1">
<div class="sub-a12">
<h4>Sample content1</h4>
</div>
</div>
<div class="sub-a2">
<div class="sub-b">
<h4>Sample content2</h4>
</div>
</div>
</div>
<button onclick="replace();"/>Click to Replace</button>
Using the following code I was expecting to get an object which contains the sub elements
var MemberDiv=document.getElementById("idProfileMainContainer");
console.log(MemberDiv.length);
Using the above code I get "undefined". I'm confused. I thought it should have an object array that contains all sub elements.
Ultimately I am trying to find the sub <div /> with the <span /> that has "Board Position" in it.
<div id="idContainer8592665" class="fieldContainer simpleTextContainer">
<div class="fieldSubContainer labeledTextContainer">
<div class="fieldLabel">
<span id="FunctionalBlock1_ctl00_ctl00_MemberForm_memberFormRepeater_ctl22_titleLabel" title="Cannot be edited, Administrator access only">Board Position</span>
</div>
<div class="fieldBody">
<span id="FunctionalBlock1_ctl00_ctl00_MemberForm_memberFormRepeater_ctl22_DropDownLabel8592665">Webmaster</span><input name="FunctionalBlock1$ctl00$ctl00$MemberForm$memberFormRepeater$ctl22$ctl03" type="hidden">
</div>
</div>
</div>
The return value of getElementById is not an array, but one node (or none) as you can find on MDN:
Returns a reference to the element by its ID
To get the element you are targetting, you could use this ES6 script:
// Select all div.fieldLabel descendants of #idContainer8592665 and find one that has
// the text content we look for.
var div = Array.from(document.querySelectorAll('#idContainer8592665 div.fieldLabel'))
.find( div => div.textContent.includes('Board Position'));
// Show the text of the div element we found
console.log(div.textContent.trim());
<div id="idContainer8592665" class="fieldContainer simpleTextContainer">
<div class="fieldSubContainer labeledTextContainer">
<div class="fieldLabel">
<span id="FunctionalBlock1_ctl00_ctl00_MemberForm_memberFormRepeater_ctl22_titleLabel" title="Cannot be edited, Administrator access only">Board Position</span>
</div>
<div class="fieldBody">
<span id="FunctionalBlock1_ctl00_ctl00_MemberForm_memberFormRepeater_ctl22_DropDownLabel8592665">Webmaster</span><input name="FunctionalBlock1$ctl00$ctl00$MemberForm$memberFormRepeater$ctl22$ctl03" type="hidden">
</div>
</div>
</div>
In ES5 compatible script it would look like this:
// Select all div.fieldLabel descendants of #idContainer8592665 and find one that has
// the text content we look for.
var parent = document.getElementById('idContainer8592665');
var div = [].filter.call(parent.getElementsByClassName('fieldLabel'),
function (div) {
return div.textContent.indexOf('Board Position') != -1;
}).pop();
// Show the text of the div element we found
console.log(div.textContent.trim());
<div id="idContainer8592665" class="fieldContainer simpleTextContainer">
<div class="fieldSubContainer labeledTextContainer">
<div class="fieldLabel">
<span id="FunctionalBlock1_ctl00_ctl00_MemberForm_memberFormRepeater_ctl22_titleLabel" title="Cannot be edited, Administrator access only">Board Position</span>
</div>
<div class="fieldBody">
<span id="FunctionalBlock1_ctl00_ctl00_MemberForm_memberFormRepeater_ctl22_DropDownLabel8592665">Webmaster</span><input name="FunctionalBlock1$ctl00$ctl00$MemberForm$memberFormRepeater$ctl22$ctl03" type="hidden">
</div>
</div>
</div>
What I've done is get the collection of span elements. I then step through this collection, checking if the textContent is 'Board Position'. If so, I walk back up the DOM tree until I reach a div node. I then print it's class (the only attribute it has in your html) and also, the outerHTML.
If there may be more that one matching element, result should be initialized to [], you should check for .length > 0 instead of == undefined, you should push the element into result result.push(elem) instead of setting it to the elem with result = elem and finally, you should iterate through the results, rather than showing the only single one.
function byId(id){return document.getElementById(id)}
function allByTag(tag,parent){return (parent == undefined ? document : parent).getElementsByTagName(tag)}
// useful for HtmlCollection, NodeList, String types - (array-like objects)
function forEach(array, callback, scope){for (var i=0,n=array.length; i<n; i++)callback.call(scope, array[i], i, array);} // passes back stuff we need
window.addEventListener('load', onDocLoaded);
function onDocLoaded(evt)
{
var spans = allByTag('span');
var result=undefined;
forEach(spans, findParentIfMatching);
if (result != undefined)
{
console.log(result.className);
console.log('*******************************');
console.log(result.outerHTML);
}
function findParentIfMatching(elem,index,collection)
{
if (elem.textContent == 'Board Position')
{
while (elem.nodeName != 'DIV')
elem = elem.parentNode;
result = elem;
}
}
}
<div id="idContainer8592665" class="fieldContainer simpleTextContainer">
<div class="fieldSubContainer labeledTextContainer">
<div class="fieldLabel">
<span id="FunctionalBlock1_ctl00_ctl00_MemberForm_memberFormRepeater_ctl22_titleLabel" title="Cannot be edited, Administrator access only">Board Position</span>
</div>
<div class="fieldBody">
<span id="FunctionalBlock1_ctl00_ctl00_MemberForm_memberFormRepeater_ctl22_DropDownLabel8592665">Webmaster</span><input name="FunctionalBlock1$ctl00$ctl00$MemberForm$memberFormRepeater$ctl22$ctl03" type="hidden">
</div>
</div>
</div>