This question already has answers here:
How to generate unique css selector for DOM element?
(7 answers)
Closed 6 years ago.
Usually, we use querySelectorAll('html:eq(0) > body:eq(0) > div.row:eq(0) > div.span4:eq(2) > p.p3:eq(2)') to get the DOM ELEMENT.
If I want to use the ELEMENT <p class="p3">target</p> with native javascript to get the selector string, what should I do?
For example:
<div class="row">
<div class="span4">
<p class="p1">p1_1<p>
<br>
<p class="p1">p1_2<p>
<p class="p1">p1_3<p>
</div>
<br>
<div class="span4">
<p class="p2">p2_1<p>
<p class="p2">p2_2<p>
<br>
<p class="p2">p2_3<p>
</div>
<p>stranger</p>
<div class="span4">
<p class="p3">p3_1<p>
<br>
<p class="p3">p3_2<p>
<br>
<p class="p3">target<p>
</div>
<div>
<div class="row">
<div class="span3">
<p class="p3">p1<p>
<br>
<p class="p3">p2<p>
<p class="p3">p3<p>
</div>
<div>
<br>
<div class="row">
<p>stranger</p>
<div>
Thses are what I think:
Taking advantage of those attributes of the element <p class="p3">target</p>, I found these are useful:
attributes
nextElementSibling
previousElementSibling
parentElement
Attribute => attributes: I can use it generate the string like div.className#idName, this is not hard to do.
Attribute => nextElementSibling, previousElementSibling: I can use it generate the string like :eq(0).
What I met as a problem is the Step3, because if there are BR between two DIV, then this two ElementSibling will return BR, it means the BR is treated as a brother of this two div who has the same class, but that is not i want.
So I tried to consider use Step3.1:
3.1. Attribute => parentElement: Use element's parent to get the eq, who it is ? 0, 1, 2.....
But I find i cannot specify targeting element is which one, because he has so many same brothers have the same tagName, and className....
I would like to know if you guys have any good ideas?
If you are trying to find the element based on the html, in this case "target", you can do this:
var elms = document.querySelectorAll('p');
var targetElm;
for (var i = 0; i < elms.length; i++) {
if (elms[i].innerHTML === 'target') {
targetElm = elms[i];
}
}
Related
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>
This question already has answers here:
Find HTML based on partial attribute
(2 answers)
Closed 1 year ago.
<div data-start-one="one"></div>
<div data-start-one="one"></div>
<div data-start-two="two"></div>
<div data-start-three="three"></div>
How can I retrieve all elements that have a jQuery attribute that starts with data-start?
For example: $("div[data-start-*]")
There's no good way to do this. You'll have to iterate over all elements that might match what you want, then filter them out.
const starts = $('div').filter(function() {
return [...this.attributes].some(
attrib => attrib.name.startsWith('data-start')
);
});
console.log(starts.length);
console.log(starts[3]);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div data-start-1="one"></div>
<div data-start-1="one"></div>
<div data-start-2="two"></div>
<div data-start-3="three"></div>
<div class="somethingElse"></div>
A much better approach would be to be able to select these elements using something else in common, like a class or another data attribute or a descendant of a container.
const starts = $('.container div');
console.log(starts.length);
console.log(starts[3]);
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container">
<div data-start-1="one"></div>
<div data-start-1="one"></div>
<div data-start-2="two"></div>
<div data-start-3="three"></div>
</div>
<div class="somethingElse"></div>
I have a tool that is used for cleaning up crappy HTML in order to make sense of the underlying structure. Having stripped class, style attributes and various Angular attributes, often the resulting markup is a series of nested <div> or <span> elements that have no attributes. What I would like to do is provide option to do a second pass where a <div> or <span> with no attributes can be removed, to flatten the structure more.
Is there a way in JavaScript to confirm that an HTML element has no attributes of any kind?
And if that is possible, how might I approach this stripping of an element?
For example, assuming I have this:
<div>
<div>
<div id="blah">
<div>
<div>
<span dir="auto">
<span>Joe Bloggs</span>
</span>
</div>
</div>
</div>
</div>
</div>
That should end up as:
<div id="blah">
<span dir="auto">
Joe Bloggs
</span>
</div>
Which I would then format to:
<div id="blah">
<span dir="auto">
Joe Bloggs
</span>
</div>
So I'd need a function that can walk the DOM and remove a div (or span) that has no attributes while leaving the inner contents intact (unless of course any of those inner elements can also be stripped for same reason).
Any pointers before I go ahead and construct a shoddy (but working) script would be appreciated!
The attributes property will tell you how many attributes an element has.
const countAttributes = element => console.log({
count: element.attributes.length,
list: [...element.attributes].map(attribute => attribute.name)
});
const divs = document.querySelectorAll('div');
divs.forEach(countAttributes);
<div></div>
<div class="one attribute"></div>
<div class="two attributes" id="second attribute"></div>
Do note that an element without attributes might still be used for something (e.g. a stylesheet might reference it in relation to other elements).
Here's how I did it.
I created a demo element, to get the elements, then I checked the number of elements, I checked if the element should be stripped.
I replaced the element with its children, and if it didn't have any, I used its text
function strip(startElement, toStrip) {
const test = document.createElement('div');
test.innerHTML = startElement.outerHTML;
[...test.querySelectorAll('*')].forEach(elem => {
if (!elem.attributes.length && toStrip.includes(elem.tagName.toLowerCase())) {
if (elem.children.length) elem.replaceWith(...elem.children);
else elem.replaceWith(elem.innerText);
} ;
});
return test.innerHTML;
}
console.log(strip(document.querySelector('div'), ['span', 'div']));
<div>
<div>
<div id="blah">
<div>
<div>
<span dir="auto">
<span>Joe Bloggs</span>
</span>
</div>
</div>
</div>
</div>
</div>
Updated Code
Here you go.
document.querySelectorAll("div").forEach((ele) => {
if (ele.attributes.length === 0) {
var fragment = document.createDocumentFragment();
while (ele.firstChild) {
fragment.appendChild(ele.firstChild);
}
ele.parentNode.replaceChild(fragment, ele);
}
});
<div>
<div>
<div id="blah">
<div>
<div>
<span dir="auto">
<span>Joe Bloggs</span>
</span>
</div>
</div>
</div>
</div>
</div>
So final output would be
<div id="blah">
<span dir="auto">
<span>Joe Bloggs</span>
</span>
</div>
Trying to replace a word that possibly will come in a foreach loop of a database items in razor view.
What I've tried so far
<section class="section bg-gray">
<div class="container">
<div class="row gap-y">
#foreach (var item in Model)
{
<div class="col-md-6 col-lg-4">
<div class="card d-block">
<p class="text-justify">#item.Text</p>
<p class="text-center mt-7">
<a class="btn btn-primary" href="#">Read more</a>
</p>
</div>
</div>
}
</div>
<script src="~/Scripts/jquery-3.3.1.min.js"></script>
<script type="text/javascript">
$(document).ready(function () {
var elements = getElementsByClassName("text-justify");
$(elements).each(function(element) {
element.innerHTML = element.innerHTML.replace(/wordToReplace/g, 'newWord');
});
});
</script>
</div>
</section>
Excuse my poor JavaScript, I'm new on front-end. I looked for similar questions but closer topics are usually about replacing instances of a word in one tag. Please help.
You don't need jQuery for this - you can use document.querySelectorAll and just replace the desired text of the elements that match the selector.
Note that I have dodgied up a text element and for the desired class and replacing justify with justified to demonstrate the usage.
let elements = document.querySelectorAll(".text-justify");
elements.forEach(function(element){
let textContent = element.innerText;
let newTextContent = textContent.replace(/justify/g, 'justified');
element.innerText = newTextContent;
})
<p class="text-justify">This is a text with the class of text-justify</p>
<p>This is a text without the class of text-justify</p>
<p class="text-justify">This is a text with the class of text-justify</p>
You don't need jQuery for this - use a simple forEach loop. I've also refactored some other parts of your code (eg you were missing document:
document.getElementsByClassName("text-justify").forEach(element => element.innerHTML = element.innerHTML.replace(/word/g, "newWord"));
But if you really want to use jQuery:
$(".text-justify").html((index, element) => element.replace(/word/g, "newWord"));
This question already has answers here:
Recursively change element type with jQuery, why it's partially working?
(2 answers)
Closed 7 years ago.
I'm trying to replace all the child elements in a certain element to DIVs.
I have the following structure:
<div id="wrapper">
<span>
<span>
<span>
<span></span>
<span></span>
</span>
</span>
</span>
</div>
I've tried replaceElements($('#wrapper')):
function replaceElements($element){
var children = $element.find("*");
var current;
for(var i=0; i<children.length; i++){
current = $(children[i]);
current.replaceWith("<div>" + current.html() + "</div>");
}
}
However this doesn't work because when it tries to change the N+1 element it all ready doesn't exist because it was replaced when doing the Nth element. N+1 is a descendant of N and thus is being changed when changing the .html() of N. Preserving element attributes is not an issue here.
How can I change all the child SPANs into DIVs, please?
You should not use the html content in the replaced element, instead need to add the dom elements to it
function replaceElements($element) {
$element.find("*").replaceWith(function() {
return $('<div></div>', {
html: this.childNodes
});
})
}
replaceElements($('#wrapper'))
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="wrapper">
<span>1
<span>2
<span>3
<span>4</span>
<span>5</span>
</span>
</span>
</span>
</div>
Note: No attributes of the elements will be copied