I am trying to wrap each span in a container. To achieve this I used a for loop to create multiple containers and then append the span to the container of the same index.
Why isnt my logic working?
Html: Two span tags (Sorry html code wont show)
Javascript:
var spans = document.getElementsByTagName('span'),
body = document.getElementsByTagName('body')[0];
for(var i = 0; i < spans.length; i++)
{
var container = document.createElement('figure');
container.setAttribute('class', 'container');
body.appendChild(container);
container.appendChild(spans[i]);
}
Edit: https://jsfiddle.net/tsrLutpg/1/
Part of the reason for the oddity is because the HTMLCollection set to spans is "live." This means, as you modify a <span>, the collection changes to reflect that change.
In this case, the collection changes the order the <span>s are listed in. As you're iterating, some may be wrapped twice moving from one <figure> to another, while others may remain unaltered when they move to an index that's already been visited.
<span>Foo</span>
<span>Bar</span>
<span>Baz</span>
for (var i = 0; i < spans.length; i++)
{
// ...
console.log(Array.from(spans).map(s => s.outerHTML));
container.appendChild(spans[i]);
}
// ["<span>Foo</span>", "<span>Bar</span>", "<span>Baz</span>"] (1 2 3)
// ["<span>Bar</span>", "<span>Baz</span>", "<span>Foo</span>"] (2 3 1)
// ["<span>Bar</span>", "<span>Foo</span>", "<span>Baz</span>"] (2 1 3)
You can avoid this by creating a static collection of the <span>s to iterate over, that doesn't change as the <span>s change.
In modern browsers, you can use Array.from() for this (similar to the above snippet).
var spans = Array.from(document.getElementsByTagName('span'));
https://jsfiddle.net/394La14t/
For compatibility, MDN offers a polyfill you can use. Or, you can call .slice() instead.
var spans = Array.prototype.slice.call(document.getElementsByTagName('span'), 0);
Here is the solution that properly wraps span tags:
var spans = document.getElementsByTagName("span");
for (var i = 0; i < spans.length; i++) {
var container = document.createElement("figure");
container.className = "container";
var span = spans[i];
span = span.parentNode.replaceChild(container, span);
container.appendChild(span);
}
If you're using jQuery library on the page, you could use next line of code:
$("span").wrap("<div class = 'outer'></div>")
Here is an alternative solution using insertBefore, before appendChild:
var containers = [];
var body = document.getElementsByTagName('body')[0];
var spans = document.getElementsByTagName('span');
for (var i = 0; i < spans.length; i++) {
containers[i] = document.createElement('figure');
body.insertBefore(containers[i],spans[i]);
containers[i].appendChild(spans[i]);
}
span {
background-color: rgb(191,191,255);
}
figure {
border: 1px solid rgb(0,0,255);
}
<span>ABC</span>
<span>ABC</span>
<span>ABC</span>
<span>ABC</span>
<span>ABC</span>
<span>ABC</span>
<span>ABC</span>
<span>ABC</span>
<span>ABC</span>
<span>ABC</span>
<span>ABC</span>
Just respect the order of your spans, instead of inserting the container after all of their spans insert it where the old span used to be. Change:
body.appendChild(container);
To:
spans[i].parentNode.insertBefore(container, spans[i]);
And now it works:
var spans = document.getElementsByTagName('span'),
body = document.getElementsByTagName('body')[0];
for(var i = 0; i < spans.length; i++)
{
var container = document.createElement('figure');
container.setAttribute('class', 'container');
spans[i].parentNode.insertBefore(container, spans[i]);
container.appendChild(spans[i]);
}
<span></span>
<span></span>
Related
My question is:
Is that possible to add the same element without rewriting the same variable.
I am creating a slider, and i need to append a div with a class slide-el into block slider.
Here is a part of code
var body, html, sliderBody, btnLeft, btnRight, i, parts, vHeight, vWidth;
//Variable definitions
var i = 0,
parts = 3,
//Main html elements
body = document.body,
html = document.element,
//viewport Height and Width
vHeight = window.innerHeight,
vWidth = window.innerWidth,
sliderBody = _id("slider"),
btnLeft = _id("btn-left"),
btnRight = _id("btn-right"),
urls = ["http://www.wallpapereast.com/static/images/pier_1080.jpg",
"http://www.wallpapereast.com/static/images/pier_1080.jpg",
"http://www.wallpapereast.com/static/images/pier_1080.jpg",
"http://www.wallpapereast.com/static/images/pier_1080.jpg"];
slide = _createEl("div");
slide.className += "slide-el";
function _id(el){
return document.getElementById(""+ el +"");
}
function _createEl(el){
return document.createElement(""+ el +"");
}
window.onload = function(){
slideLayout();
}
function slideLayout(){
for(var i=0; i < urls.length; i++){
sliderBody.appendChild(slide);
}
}
The problem is that I can't append the same element that many times. It just creates one element instead of 4.
For you to understand better I made a fiddle:
https://jsfiddle.net/ud7dvn3z/
appendChild will remove the node from wherever it is before appending it to its new location, so you need to make copies of the node instead. You can use cloneNode for that. The true makes cloneNode perform a deep clone, i.e. with all its child nodes.
for(var i = 0; i < urls.length; i++){
sliderBody.appendChild(slide.cloneNode(true));
}
Okey guys! I found an answer. I have to put
slide = _createEl("div");
slide.className += "slide-el";
into for loop.
Now it looks like this:
for(var i=0; i < urls.length; i++){
slide = _createEl("div");
slide.className += "slide-el";
sliderBody.appendChild(slide);
}
I would like to create list of divs,
one of the divs should like this:
<div onclick="myFunc(this, 'arrayValue')"></div>
I want to achieve it by iterating through my array and passing index value to my function. One of the solutions that worked was using setAttribute :
for(var i = 0; i < array.length; i++){
var div = document.createElement("div");
div.setAttribute("onclick", 'myFunc(this, \''+array[i]+'\')');
myDiv.appendChild(div);
}
The only problem is that this part looks really ugly: \''+array[i]+'\')'
Is there other way to achieve this? That would look like this or something similar:
div.setAttribute("onclick", myFunc(this, array[i]);
btw. other solutions: when I used div.onclick the onclick attr was not visible on div (result: <div></div>), possibly I did something wrong.
var array = ['bla1','bla2'];
var myFunc = function(value) {
alert('click : '+ value);
};
var myDiv = document.getElementById('myDiv');
for(var i = 0; i < array.length; i++){
var div = document.createElement("div");
div.className = 'bla';
myDiv.appendChild(div);
div.addEventListener('click', myFunc.bind(this, array[i]));
}
.bla {
border: 1px solid black;
width: 20px;
height: 20px;
margin: 10px;
}
<div id="myDiv"></div>
Is that you want ?
My question is:
Is that possible to add the same element without rewriting the same variable.
I am creating a slider, and i need to append a div with a class slide-el into block slider.
Here is a part of code
var body, html, sliderBody, btnLeft, btnRight, i, parts, vHeight, vWidth;
//Variable definitions
var i = 0,
parts = 3,
//Main html elements
body = document.body,
html = document.element,
//viewport Height and Width
vHeight = window.innerHeight,
vWidth = window.innerWidth,
sliderBody = _id("slider"),
btnLeft = _id("btn-left"),
btnRight = _id("btn-right"),
urls = ["http://www.wallpapereast.com/static/images/pier_1080.jpg",
"http://www.wallpapereast.com/static/images/pier_1080.jpg",
"http://www.wallpapereast.com/static/images/pier_1080.jpg",
"http://www.wallpapereast.com/static/images/pier_1080.jpg"];
slide = _createEl("div");
slide.className += "slide-el";
function _id(el){
return document.getElementById(""+ el +"");
}
function _createEl(el){
return document.createElement(""+ el +"");
}
window.onload = function(){
slideLayout();
}
function slideLayout(){
for(var i=0; i < urls.length; i++){
sliderBody.appendChild(slide);
}
}
The problem is that I can't append the same element that many times. It just creates one element instead of 4.
For you to understand better I made a fiddle:
https://jsfiddle.net/ud7dvn3z/
appendChild will remove the node from wherever it is before appending it to its new location, so you need to make copies of the node instead. You can use cloneNode for that. The true makes cloneNode perform a deep clone, i.e. with all its child nodes.
for(var i = 0; i < urls.length; i++){
sliderBody.appendChild(slide.cloneNode(true));
}
Okey guys! I found an answer. I have to put
slide = _createEl("div");
slide.className += "slide-el";
into for loop.
Now it looks like this:
for(var i=0; i < urls.length; i++){
slide = _createEl("div");
slide.className += "slide-el";
sliderBody.appendChild(slide);
}
I have the following list hierarchy:
<ul id="ulid">
<li><a><div class="mydiv">content</div></a></li>
<li><a><div class="mydiv">content</div></a></li>
...
</ul>
I want to add some css rules to the div and this is what i've tried so far:
var myul = document.getElementById("ulid");
var myli = myul.getElementsByTagName("li");
for(var i = 0; i < myli.length; i++) {
//myli[i].parentNode.style.display = "none"; // that works
var links = myli[i].getElementsByTagName("a");
for(var ii = 0; ii < links.length; ii++) {
links[ii].parentNode.style.display = "none"; // doesnt work
}
}
I can hide the li items but cant do the same for a So i cant reach the div. What am i doing wrong here?
EDIT: getElementsByClassName seems not working in greasemonkey scripts as it simply works in Emmanuel N's fiddle.
Your code seems to work. Check out this Fiddle
var myul = document.getElementById("ulid");
var myli = myul.getElementsByTagName("li");
for(var i = 0; i < myli.length; i++)
{
var links = myli[i].getElementsByTagName("a");
for(var ii = 0; ii < links.length; ii++)
{
links[ii].parentNode.style.display = "none";
}
}
Your code actually does work, but I don't think it does what you're intending it to do. The last line: links[ii].parentNode.style.display = "none" will actually hide the parent node of the a tag (i.e. the li) tag, rather than the div. parentNode will go one level UP, not down.
Instead of trying to get myli[i].getElementsByTagName("a") and then working down to the div, why not myli[i].getElementsByTagName("div"), and then simply do:
var myul = document.getElementById("ulid");
var myli = myul.getElementsByTagName("li");
for(var i = 0; i < myli.length; i++) {
//myli[i].parentNode.style.display = "none"; // that works
var links = myli[i].getElementsByTagName("div");
for(var ii = 0; ii < links.length; ii++) {
links[ii].style.display = "none";
}
}
Of course, there are many more efficient ways to do it. You already have classnames on the divs, so
document.getElementsByClassName("mydiv");
would work just as well.
Or, if you use jQuery, you can do the same thing without having to iterate explicitly:
$("div.mydiv").css(etc.); // style this however you want
If you aren't opposed to using jQuery, the following would hide your divs for you.
$(document).ready(function () {
var myDivs = $('div.mydiv');
for(var eachDiv in myDivs) {
$(eachDiv).hide();
}
});
I often use the CSS universal selector to reset the dimensions in my HTML document:
* {
border: 0;
margin: 0;
padding: 0;
}
Can this be done with JavaScript too?
For normal HTML elements there is the style property.
But how to speak to the universal selector?
getElementsByTagName("*") will return all elements from DOM. Then you may set styles for each element in the collection:
var allElements = document.getElementsByTagName("*");
for (var i = 0, len = allElements.length; i < len; i++) {
var element = allElements[i];
// element.style.border = ...
}
You don't need to iterate all the elements. You can demand this operation to the CSS engine of your browser. Something like that:
;(function(exports) {
var style = document.querySelector("head")
.appendChild(document.createElement("style"));
var styleSheet = document.styleSheets[document.styleSheets.length - 1];
styleSheet.insertRule("* {}", 0);
exports.universal = styleSheet.cssRules[0];
}(window));
From now, you have a window.universal object that you can use to style all the elements. For instance:
window.universal.style.border = "1px solid red";
Of course you don't need to create at runtime the <style> tag. You can always have that in plain HTML too.
You can test it running this snippet:
;(function(exports) {
var style = document.querySelector("head")
.appendChild(document.createElement("style"));
var styleSheet = document.styleSheets[document.styleSheets.length - 1];
styleSheet.insertRule("* {}", 0);
exports.universal = styleSheet.cssRules[0];
}(window));
console.log("universal" in window); // true
window.universal.style.border = "1px solid red";
<div>
Hello
<span>World</span>
</div>
In raw javascript you can do this:
document.getElementsByTagName('*')
but I wouldn't recommend adding css to all elements using js.
Thanks VisioN for the solution! I just remembered that you can do the same with the new JavaScript Query Selector API:
var allElements = document.querySelectorAll('*');
for (var i = 0; i < allElements.length; i++) {
var element = allElements[i];
element.style.border = '0px';
element.style.margin = '0px';
element.style.padding = '0px';
}