I like the usage of append in D3, and I'm looking for prepend.
Does this exist in D3?
You can use
selection.insert(newElement[, anotherExistingElement])
For example:
selection.insert("div",":first-child")
The above code will insert a div before the first child of selected element. Check documentation to learn more.
Another possible way of inserting elements before any node (including plain texts):
var parentEl = d3.select("div").node();
parentEl.insertBefore(document.createElement("div"), parentEl.childNodes[0]);
<script src="https://d3js.org/d3.v3.min.js"></script>
<div>
This is a plain text
<a></a>
</div>
Selection.lower()
selection.lower() will place an element as the first child of its parent.
Together with d3's append, selection.append().lower() can replicate jQuery's prepend
Since D3 v4+, D3 has both selection.raise() and selection.lower() methods. These are used most frequently to move elements in an SVG so that certain elements appear overtop of others, where ordering of SVG elements in the DOM determines draw order. But, they can be used for any element in the DOM.
Here's a quick demonstration using divs and paragraphs:
var div = d3.select("div");
div
.append("p")
.text("Inserted")
.lower();
console.log(div.html());
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div id="div">
Text
<p> Child Paragraph </p>
</div>
The snippet takes a div with the following contents:
Text
<p> Child Paragraph </p>
And uses d3 to append a new paragraph and then lower it so that the structure is as follows:
<p>Inserted</p>
Text
<p> Child Paragraph </p>
And for comparison with with jQuery's prepend:
var div = $("div");
div
.prepend("<p>Inserted</p>");
console.log(div.html());
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="div">
Text
<p> Child Paragraph </p>
</div>
More Info
The selection.lower() is implemented as such (see docs for more info):
selection.each(function() {
this.parentNode.insertBefore(this, this.parentNode.firstChild);
});
Related
I have some HTML that can contain <span>s with a specific class (let's call it marker). These spans can be anywhere in the document under a particular <div> - as direct children, or nested arbitrarily deeply in other nodes.
I then have a particular piece of text selected by the use (so I can use a window.getSelection() call to find the anchor node from Selection.anchorNode). What I want to find out, using Javascript, and jQuery as needed, is the last marker <span> to occur in the documents before that selection. For example:
<div class="container">
<div>
<div>
<span>Some text<span class="marker">Marker 1</span></span>
</div>
<div>
<div>
<span>Foo</span>
<span>THIS IS THE SELECTION ANCHOR NODE</span>
</div>
<span class="marker">Marker 2</span>
</div>
</div><!-- end of container -->
would find Marker 1, even though they are "cousins".
Is there a "standard" approach to determining the relative "linear" positions of an element in the DOM so I can decide if one element is "before" the other?
I am not concerned with the position on the page (x, y), so things like CSS order do not matter.
Things I have thought of, but seem suboptimal:
traversing the parents of each .marker (and the selection span) using [closest()][2] and constructing some kind of lexicographic ordering of nodes, which seems expensive and error-prone
traversing parents of .markers and storing lists of the spans found within
Both of these seem like they need a lot of book-keeping and manual DOM traversal for something that sounds like the DOM already knows (since the document has its specific order).
You can use the Node.prototype.compareDocumentPosition method to find out if an element is prior to an other one:
const anchor = document.getElementById("anchor");
const markers = document.querySelectorAll(".marker"); // get all the markers in DOM order
const previous = [...markers].filter( // get only the markers before
(elem) => anchor.compareDocumentPosition(elem) === Node.DOCUMENT_POSITION_PRECEDING
).pop(); // get the last one
console.log( previous );
<div class="container">
<div>
<div>
<span>Some text<span class="marker">Marker 0</span></span>
<span>Some text<span class="marker">Marker 1</span></span>
</div>
<div>
<div>
<span>Foo</span>
<span id="anchor">THIS IS THE SELECTION ANCHOR NODE</span>
</div>
<span class="marker">Marker 2</span>
</div>
</div><!-- end of container -->
If I understand you correctly, this should get you there:
let xpath = '(//span[#class="marker"][not(.//preceding::*[contains(.,"SELECTION ANCHOR")])])[last()]',
result = document.evaluate(xpath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
console.log(result.textContent);
How to get all the HTML nodes having text in an optimal way without having to loop through every node?
In other words, grab all HTML nodes having visible text.
For example, if I have a dom as below
<div>
<span>Hello This is a Text Span</span>
<div>
<p> This is a text Paragraph</p>
<button> This is Button Label</button>
</div>
<div> This is also a visible text</div>
</div>
I should select
span having text Hello This is a Text Span
p having text This is a text Paragraph
button having text This is Button Label
div having text This is also a visible text
The outermost div in the above example doesn't have text of its own so should not be part of the result.
Edit: What problem am I trying to solve?
The framework I use escapes HTML characters in labels of fields, buttons, headings etc.
For example: < is converted to & lt;'
So I am trying to write a client side code which triggers after the page is completely rendered which will unescape all the HTML texts to a readable format.
Any help will be appreciated.
Thanks in advance
The DOM property holds a numeric code indicating the node's type; text nodes use the code 3, So you can find those text nodes by filtering them having nodeType 3.
Wrap your all nodes in a div by giving a class.
Select your content by it's class like this: $(".getTextNodes").contents();.
Filter contents having nodeType 3.
selectedElement = $(".getTextNodes").contents();
textNodes = selectedElement.filter(function() {
return this.nodeType === 3;
});
console.log(textNodes);
<script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
<div class="getTextNodes">
<span>Hello This is a Text Span</span>
<div>
<p> This is a text Paragraph</p>
<button> This is Button Label</button>
</div>
<div> This is also a visible text</div>
</div>
Check this link out to read more.
I'm using just only vanilla js
My algorithm is just select all element that has no element inside it
It means select all element that has directly Text Node
let el = document.querySelectorAll('div,span,p,button');
var arr = [];
el.forEach(function(m){
if (m.querySelectorAll('div,span,p,button').length == 0){
arr.push(m)
console.log(m)
}
})
// console.log(arr)
<div>
<span>Hello This is a Text Span</span>
<div>
<p>This is a text Paragraph</p>
<button> This is Button Label</button>
</div>
<div>This is also a visible text</div>
</div>
Jsfidle link click here
There's no css selector to get your needs and looping is only the solution.
This question already has answers here:
Using .text() to retrieve only text not nested in child tags
(30 answers)
Closed 5 years ago.
Could some one give me some guidance on what's the best way to do this.
I'm trying to get all the text which is after ".main"
I know this might be simple, but it's been picking at my brain all day just before Christmas. So i thought instead of stressing myself out, I would look for some guidance.
The example code only brings back the Text in P tag but i'd like to bring back Text not in it's own element and the p tag
console.log($("#container").find(".main").next().text());
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="container">
<div class="main"> WOOP IN MAIN </div>
Text not in it's own element
<p> Text in P tag </p>
</div>
The simplest way to achieve this is to clone() the container, remove the .main element from it, then get the text(), like this:
var text = $("#container").clone().find('.main').remove().end().text();
console.log(text.trim());
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="container">
<div class="main"> WOOP IN MAIN </div>
Text not in it's own element
<p> Text in P tag </p>
</div>
You could alternatively recursively traverse through the DOM nodes that follow the .main element, but this is much more complicated and gives the same result.
It's because Text not in it's own element is considered a text node, therefore next() will target the <p/> tag, being that it's an HTMLElement. If you were to go native you'd use a combination of nextSibling, which is agnostic of the two node types and nextElementSibling, which as it's method name implies, grabs the next sibling element:
const main = document.querySelector('.main');
const txt = [
main.nextSibling.textContent.trim(),
main.nextElementSibling.textContent.trim()
];
console.log(txt)
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="container">
<div class="main"> WOOP IN MAIN </div>
Text not in it's own element
<p> Text in P tag </p>
</div>
I have some div tags which has some text & elements in it & I want to remove those div's, They are looks like this
<div style="font-family:verdana;font-size:12px;">
Example
example
</div>
There are many div's like this & I want to remove them all with using jQuery or javascript
If the elements have nothing in common such as a class, you can remove it by using the :contains and remove() method.
$("div:contains('Example')").remove()
Full example shown below:
$("div:contains('Example')").remove()
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<div>
Example
</div>
<div>
Darren
</div>
If the elements do have something in common you could use the class selector.
$(".common-class").remove();
Based on Darren's answer, if you want to be extra sure (as :contains will match and delete any div containing the word example), you can make sure it's a div that has an anchor with that same example as children, then go back to the parent and remove it.
If this doesn't work, please paste a few more divs so we can see a common pattern and target it the safest way possible.
$(document).ready(function(){
$('#remove').click(function(e){
$("div:contains('Example')").children("a:contains('example')").parent("div:contains('Example')").remove()
})
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div style="font-family:verdana;font-size:12px;">Example example</div>
<div style="font-family:verdana;font-size:12px;">Don't remove example</div>
<div style="font-family:verdana;font-size:12px;">Example don't remove</div>
<button id="remove">
Remove undesired divs
</button>
I am looking for a Javascript solution for this problem. I have the following HTML:
<div id = "container">
<div id = "data">
<div>
<h3> Address</h3>
<b>Expand...</b>
<div id="content">ul. Pomorska</div>
</div>
<div>
<h3> Telefon </h3> <b>Expand...</b>
<div id="content">26565352</div>
</div>
<div>
<h3>Email</h3>
<b>Expand...</b>
<div id="content">asdasdag#aga.com</div>
</div>
</div>
</div>
I would like to hide the content div when an onclick Expand is made. So far I have made a function which hides the content divs and tries to assign an event handler to the node.
function hideinfo() {
var node = document.getElementById("data");
var contactdata = node.getElementsByTagName("div");
for(var i=0; i<contactdata.length;i++) {
if(contactdata[i].id == "content") {
alert(contactdata[i].previousSibling.innerHTML);
contactdata[i].previousSibling.addEventListener('click',ShowHide,false);
contactdata[i].style.display="none";
}
}
}
The problem is that the alert displays undefined. Why can't it see the node? Is there a better way to do this in Javascript?
Because previousSibling is most likely the text node before the div element. You probably want to use previousElementSibling instead :)
In most browser today, querySelectorAll, which lets you use CSS selectors for finding elements, is also a good alternative (IE8+)
The previousSibling property returns the previous sibling node (the previous node in the same tree level) of the selected element
which returns in your case the TEXT node.
As you can see in this jsfiddle: http://jsfiddle.net/Xu383/
alert(contactdata[i].previousSibling.nodeName);
You are better of using the querySelectorAll.
Also you can't have multiple divs with the SAME id, use class instead.