Add div as only child of a node - javascript

I am trying to add div as only child to a node. and if that node already has any children make them children of the div.
original DOM looks like:
<a href = "">
<span id="gsp" > </span>
<span id="gbsp"> some text </span>
</a>
I want the output to be:
<a href = "">
<div id="oaa_web_accessibility_highlight">
<span id="gsp" > </span>
<span id="gbsp"> some text </span>
</div>
</a>
instead, below snippet gives me:
<a href = "">
<div id="oaa_web_accessibility_highlight"> </div>
<span id="gsp" > </span>
<span id="gbsp"> some text </span>
</a>
I am trying to use the below snippet, but it is not working.
var new_div_element = this.document.createElement('div');
new_div_element.id = 'oaa_web_accessibility_highlight';
node.insertBefore(new_div_element, node.childNodes[0]);
for (var i =0; i < node.childNodes.length; i++) {
if (i == 0) continue;
node.childNodes[0].appendChild(node.childNodes[i]);
}
please can anyone help me on this?

Do it like this:
var new_div_element = document.createElement('div');
new_div_element.id = 'oaa_web_accessibility_highlight';
while (node.firstChild) {
new_div_element.appendChild(node.firstChild);
}
node.appendChild(new_div_element);
This empties the children of node into new_div_element, and then appends new_div_element to the now empty node.
It doesn't use innerHTML, so you're not destroying any state of the elements being transferred.
FYI, the reason yours didn't work properly was that your loop is incrementing i, but you're removing elements from the .childNodes list when you do the .appendChild.
Since .childNodes is a live list, its content is updated when you move one of its items to a different location. As a result, after the first child is removed, the second child takes its place at that index. Since your i is incremented, it skips passed the one(s) that were relocated in the list.

If you're willing to use jQuery, it has a wrapAll method that does this for you.
$(node).children().wrapAll('<div id="oaa_web_accessibility_highlight"></div>');

Related

Getting text content of a div element excluding its children

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 ''.

Only fire one onmouseover when hovering over multiple elements

I'd like to have basic code like the following:
<span onmouseover="alert('hi')">Hello, <span onmouseover="alert('hello')">this</span> is a test</span>
However, I'd like to keep it from firing both of these events if both are being hovered over; e.g. if I hover over "this" it should fire only its event and alert "hello." How can I do this?
Thank you in advance!
$(".container").hover(function(){
alert($(this).attr("id"));
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<span class="container" id="hi">
Hello,
</span>
<span class="container" id="hello">
this
</span>
<span class="container" id="hi">
is a test
</span>
I am going to assume that the overlapping elements are not the same size. I.e one is bigger than the other.
HTML and inline js:
<span class="container" id="hi">
Hello,
</span>
<span class="container " id="hello">
this </span>
<script>
var hello =
document.getElementById("hello");
var this =
document.getElementById
("this");
hello.addEventListener("click
",pop("hello"));
this.addEventListener("click",pop(" hi");
function pop(string) {
window.alert(string);
}
<\script>
That being said very little is mentioned about the nature of the elements this and hello. Op plz show your CSS and update ques
Here's the relevant portion of what I ended up using. I used JQuery.
var mouseHovered = function(element) {
//get all hovered spans
var hoveredElements = $("span:hover");
//get the element with the smallest text
var smallestElement;
for(var i=0; i<hoveredElements.length; i++) if(!smallestElement || hoveredElements[i].textContent.length < smallestElement.textContent.length) smallestElement = hoveredElements[i];
//if this is the smallest text in the elements
if(element == smallestElement) {
//log the text
console.log(element.textContent);
}
}
You need to prevent Event bubbling when you hover/click on inner span.
This can be done using event.stopPropagation().
Look at two solutions provided at this JSFiddle.
Solution 1 - Use of e.stopPropagation() in the handler function innerSpan().
Solution 2 - Use of event.stopPropagation() in inline onclick event.
<span onclick="alert('Outer span');">
Outer
<span onclick="event.stopPropagation(); alert('Inner span');">
Inner
</span>
</span>

Javascript targeting the parent div from a string of text

<div class="myDiv">
<span class="mySpan">SAMPLE TEXT A</span>
<span class="mySpan">SAMPLE TEXT B</span>
</div>
I'm struggling to get the right javascript to hide "myDiv" if the text in "mySpan" is "SAMPLE TEXT A".
Somehow like this (you will not see anything, because <div class="myDiv"> is hidden already):
var spans = document.getElementsByClassName('mySpan')
for(var i = 0; i < spans.length; i++) {
if(spans[i].innerHTML === 'SAMPLE TEXT A') spans[i].parentElement.style.display = 'none';
}
<div class="myDiv">
<span class="mySpan">SAMPLE TEXT A</span>
<span class="mySpan">SAMPLE TEXT B</span>
</div>
UPDATE
"What is the significance of the 'for' statement?"
document.getElementsByClassName('mySpan') will select ALL elements with class mySpan, think about it as an array. Then i iterate through all selected span's and finding the case, where innerHTML is equal to our specific string.
"In the if statement, how would I specify just the parent div and not every element with class "myDiv"? There are other elements on the page with that class."
If so, you need to use parentElement (I have updated my answer with needed row).

I need to create divs with nested divs inside it

have a look at the code
Html Code :
<div class="container content-rows" id="contentdisplay">
<div class="col-md-1" id="snocontent">abcd</div>
<div class="col-md-3" id="pgnamecontent">abcd</div>
<div class="col-md-3" id="cmpcontent">abcd</div>
<div class="col-md-2" id="datecontent">abcd</div>
<div>
<button onclick="createdivs()"><span class="glyphicon glyphicon-king" class="addtrainersbutton" id="addtrainersbutton" title="Add Trainers"></span></button>
<button onclick="edit_program()"><span class="glyphicon glyphicon-pencil" id="editprogrambutton" title="Edit Program"></span></button>
<span class="glyphicon glyphicon-user" id="assigntrainersbutton" title="Assign Trainers for the Program"></span>
<span class="glyphicon glyphicon-remove" id="deleteprogrambutton" title="Delete the Program"></span>
</div>
Javascript Code:
function createdivs() {
var i;
for (i = 0;i < 10;i++)
{
divc = "<div>.container.content-rows";
var list = document.createElement("divc");
document.body.appendChild(list);
list.className = "proglist";
}
}
I have a few questions, please clarify them or explain with code:
10 divc's are created but there are no other div elements inside them, only the first div has some content in it.. other divs are just created and they dnt even occupy space inside the webpage
I want the div to be aligned to the first div, ie., instead of document.body.appendChild I need something like document.div.appendChild, whereas the created divs should be appeneded with the first div..
Please let me know how can I get them with explanation.. Thank you in advance..
10 divc's are created but there are no other div elements inside them, only the first div has some content in it.. other divs are just created and they dnt even occupy space inside the webpage
Space isn't occupied because they don't have any content added to them. You add content to an element just like you do with document.body - appendChild() for example.
I want the div to be aligned to the first div, ie., instead of document.body.appendChild I need something like document.div.appendChild, whereas the created divs should be appeneded with the first div..
Elements have such a method:
function createdivs() {
var i;
for (i = 0; i < 10; i++) {
divc = "<div>.container.content-rows";
var divc = document.createElement("div");
divc.classList.add("container");
divc.classList.add("content-rows");
divc.classList.add("proglist");
divc.textContent = "I'm div #" + i;
document.getElementById('contentdisplay').appendChild(divc);
createNestedDivs(divc);
}
}
function createNestedDivs(selector) {
function appendToNode(node, content) {
// ideally content would also be a Node, but for simplicity,
// I'm assuming it's a string.
var inner = document.createElement('span');
inner.textContent = content;
node.appendChild(inner);
}
if (selector instanceof Node) {
appendToNode(selector, "inner");
return;
}
var selected = Array.prototype.slice.call(document.querySelectorAll(selector));
selected.forEach(function(el) {
appendToNode(el, "inner");
});
}
<div class="container content-rows" id="contentdisplay">
<div class="col-md-1" id="snocontent">abcd</div>
<div class="col-md-3" id="pgnamecontent">abcd</div>
<div class="col-md-3" id="cmpcontent">abcd</div>
<div class="col-md-2" id="datecontent">abcd</div>
<div>
<button onclick="createdivs()"><span class="glyphicon glyphicon-king" class="addtrainersbutton" id="addtrainersbutton" title="Add Trainers"></span>
</button>
<button onclick="edit_program()"><span class="glyphicon glyphicon-pencil" id="editprogrambutton" title="Edit Program"></span>
</button> <span class="glyphicon glyphicon-user" id="assigntrainersbutton" title="Assign Trainers for the Program"></span>
<span class="glyphicon glyphicon-remove" id="deleteprogrambutton" title="Delete the Program"></span>
</div>
The createNestedDivs() function takes care of if you want to append another child to each created element. You can pass it a Node or string and it will treat them as expected. createNestedDivs() works by the same principles as the modifications I made to createdivs(). The case for handling a Node is simple enough, but the string handling is a little less clear:
The Array.prototype.slice.call is necessary because document.querySelectorAll returns a NodeList, which isn't an array, and I can't use Array.prototype.forEach() on it.
Also, related reading here: http://www.w3.org/wiki/The_principles_of_unobtrusive_JavaScript

jQuery Replicate an existing Div Multiple Times

I am building a search query which gives me results.
I have a template ready for the item inside a hidden div. What I want to do is replicate the template n number of times using jQuery.
So For example:
I search for flights and I get 5 search results, I need to replicate the below div template 5 Times
<div id="oneWayFlightElement" class="displayNone">
<div id="flightIndex1" class="flightDetailElement boxShadowTheme">
<div id="flightDetailsLeftPanel1" class="flightDetailsLeftPanel marginBottom10">
<div class="fullWidth marginTop10">
<span id="flightPriceLabel1" class="headerFontStyle fullWidth boldFont">Rs 9500.00</span><hr/>
<div id="homeToDestination1" class="flightBlockStyle">
<span id="flightNumberFromHome1" class="fontSize16">AI-202</span><br/>
<span id="flightRouteFromHome1" class="fontSize26">PNQ > DEL</span><br/>
<span id="flightDepartTimeFromHome1" class="fontSize26">Depart: 10.00 AM</span><br/>
<span id="flightArrivalTimeFromHome1" class="fontSize26">Arrive: 12.00 PM</span><br/>
</div>
<div id="destinationToHome1" class="flightBlockStyle">
<span id="flightNumberToHome1" class="fontSize16">AI-202</span><br/>
<span id="flightRouteToHome1" class="fontSize26">PNQ > DEL</span><br/>
<span id="flightDepartTimeToHome1" class="fontSize26">Depart: 10.00 AM</span><br/>
<span id="flightArrivalTimeToHome1" class="fontSize26">Arrive: 12.00 PM</span><br/>
</div>
</div>
</div>
<div id="flightDetailsRightPanel1" class="flightDetailsRightPanel textAlignRight marginBottom10">
<img src="images/flightIcon.png" class="marginRight10 marginTop10 width40"/><br/>
<button class="marginRight10 marginBottom10 width40 bookNowButtonStyle">Book Now</button>
</div>
</div>
</div>
Inside this div for 5 times
<div id="searchFlightResultDiv" class="fullWidth" style="border:solid">
</div>
Is there a better way to do that rather than string appending in jQuery?
Thanks,
Ankit Tanna
You'll need to wrap your template div (#flightIndex1) in a container with a unique id attribute. Then, you take the contents of that container (a template for a single record), and append it to your results div (#searchFlightResultDiv) using some type of loop based on the number of results received.
Basically,
HTML:
<!-- Here's your template -->
<div class="displayNone" id="oneWayFlightElement">
<!-- This id (singleResult) is important -->
<div id="singleResult">Result</div>
</div>
<!-- Container for the results -->
<div id="results"></div>
Javascript:
//Get the number of results.
//This can be sent from your API or however you're getting the data.
//For example, in PHP you would set this to $query->num_rows();
var count = 5;
//Start a for loop to clone the template element (div#singleResult) into div#results 'count' times.
//This will repeat until the number of records (count) has been reached.
for (i = 1; i <= count; i++) {
//Append the HTML from div#thingToRepeat into the #results.
$('#results').append($('#singleResult').clone());
}
Here's a JSFiddle to show you how it works. You can play with it and tweak it if necessary.
I can't in good conscious complete this post without telling you the downsides of this. Doing it this way is majorly frowned upon in the web development community and is super inefficient. It may be good for practice and learning, but please do take a look at and consider a javascript templating framework like moustache or handlebars. It does this same thing but way more efficiently.
Hope this was helpful!
function populateResult(resCount) {
resCount = typeof resCount === 'number' ? resCount : 0;
var res = [];
var templateEle = $('#oneWayFlightElement');
for(var i = 0; i < resCount; ++i)
res.push(templateEle.clone().removeAttr('id class')[0]);
$('#searchFlightResultDiv').html(res);
}
populateResult(5);
We use an array res to hold the DOM elements as we loop and finally sets it to the target div using html method. We don't need a JQuery object here as the html method accepts any array like object. In this way we can minimize browser reflows. Here is the JSFiddle

Categories