I'm practicing basic JS skills by setting up little exercises for myself. In this one, I have a list of <a>s inside a div. The aim of the exercise is to wrap each <a> in a div. I'm using replaceChild in this instance.
Oddly (to me at least) the script works for the first three links, but after that throws an error:
Uncaught TypeError: Cannot read property 'parentNode' of undefined
I can't tell why the script partly works. Can anyone see what I'm doing wrong here? Here's the code I'm using:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<style media="all">
div div {padding: 10px; background: #e7e7e7; margin: 5px;}
</style>
</head>
<body>
<div>
link
link
link
link
link
link
</div>
<script>
var links = document.getElementsByTagName("a");
for (var i=0, ii=links.length; i<ii; i++)
{
var container = document.createElement("div");
links[i].parentNode.replaceChild(container, links[i]);
container.appendChild(links[i]);
}
</script>
</body>
</html>
and here's an online version: http://codepen.io/anon/pen/Lpuky
I've tried the few debugging techniques that I know of and read about this error message, but haven't worked out what's wrong here. Seems funny to me that it works for 3 of the 6 links.
The collection links is NodeList and is live.
Since you are replacing them, they are disappearing from the collection and our index into them is no longer pointing to anything.
You're modifying the nodelist as you iterate over it. Use the Array slice method to make a copy of the list:
var linksCopy = Array.prototype.slice.call(links);
for (var i=0; i<linksCopy.length; i++)
{
var container = document.createElement("div");
linksCopy[i].parentNode.replaceChild(container, linksCopy[i]);
container.appendChild(linksCopy[i]);
}
Regarding your own follow-up answer: if your objective was simply to find the easiest way to wrap the <a>s in <div>s, rather than to practice with createElement, replaceChild or appendChild or any of the other methods, this would be it:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Demo</title>
<style>
div div {
padding: 10px;
background: #e7e7e7;
margin: 5px;
}
</style>
</head>
<body>
<div>
link
link
link
link
link
link
</div>
<script>
var links = document.querySelectorAll("a");
for (var i=0; i<links.length; i++) {
links[i].outerHTML = '<div>'+links[i].outerHTML+'</div>';
}
</script>
</body>
</html>
.
Live demo here: http://jsbin.com/jasoho/1/edit?html,output. Another advantage of the outerHTML method is that it doesn't change the nodeList. So you can also use getElementsByTagName in stead of querySelectorAll.
As a follow up to this, I often hear that querySelectorAll() is different in that it returns a static Nodelist rather than an array, so I thought that might come in handy here, and indeed it does:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<style media="all">
div div {padding: 10px; background: #e7e7e7; margin: 5px;}
</style>
</head>
<body>
<div>
link
link
link
link
link
link
</div>
<script>
var links = document.querySelectorAll("a");
for (var i=0, ii=links.length; i<ii; i++)
{
var container = document.createElement("div");
links[i].parentNode.replaceChild(container, links[i]);
container.appendChild(links[i]);
}
</script>
</body>
</html>
Also, an alternative to Array.prototype.slice.call(links) is [].slice.call(links):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<style media="all">
div div {padding: 10px; background: #e7e7e7; margin: 5px;}
</style>
</head>
<body>
<div>
link
link
link
link
link
link
</div>
<script>
var links = document.getElementsByTagName("a");
var linksCopy = [].slice.call(links);
for (var i=0; i<linksCopy.length; i++)
{
var container = document.createElement("div");
linksCopy[i].parentNode.replaceChild(container, linksCopy[i]);
container.appendChild(linksCopy[i]);
}
</script>
</body>
</html>
And another option again is to use [].forEach.call():
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<style media="all">
div div {padding: 10px; background: #e7e7e7; margin: 5px;}
</style>
</head>
<body>
<div>
link
link
link
link
link
link
</div>
<script>
[].forEach.call(document.querySelectorAll('a'), function(el) {
var container = document.createElement("div");
el.parentNode.replaceChild(container, el);
container.appendChild(el);
});
</script>
</body>
</html>
Yet another option, using Array.from():
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<style media="all">
div div {padding: 10px; background: #e7e7e7; margin: 5px;}
</style>
</head>
<body>
<div>
link
link
link
link
link
link
</div>
<script>
var links = document.getElementsByTagName("a");
var linksCopy = Array.from(links);
for (var i=0; i<linksCopy.length; i++)
{
var container = document.createElement("div");
linksCopy[i].parentNode.replaceChild(container, linksCopy[i]);
container.appendChild(linksCopy[i]);
}
</script>
</body>
</html>
Related
I made a script that when I click on the container it hides a list. However, what I have been trying to do is that the list should hide when clicking outside the container and not inside. I have been looking for answers but nothing really worked for me as I use classes. Does someone know a solution?
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width" />
<title>Hiding the list</title>
</head>
<body>
<h1>Hiding the list</h1>
<div class="list">Hide the list by clicking outside
<li>Text1</li>
<li>Text2</li>
<li>Text3</li>
</div>
<div class="list">Hide the list by clicking outside
<li>Text1</li>
<li>Text2</li>
<li>Text3</li>
</div>
</body>
</html>
<style>
.list {
width: 50%;
height: 100%;
background-color: #f2f2f2;
}
li {
}
</style>
<script>
function hide_list() {
var children = this.children;
for (let i = 0; i < children.length; i++) {
children[i].style.display = "none";
}
}
document.querySelectorAll(".list").forEach(function (elem) {
elem.addEventListener("click", hide_list);
});
</script>
I want to use HTML import so I created two files.
File1:
<!DOCTYPE html>
<html>
<head>
<style>
div {
height: 300px;
width: 300px;
background-color: yellow;
}
</style>
</head>
<body>
<div></div>
</body>
</html>
File2:
<!DOCTYPE html>
<html>
<head>
<link rel='import' href='test.html' id='LINK'>
<script>
var LINK = document.getElementById('LINK');
var test = LINK.import;
var content = document.importNode(test.content, true);
document.body.appendChild(content);
</script>
</head>
<body>
</body>
</html>
I should see a yellow square when I execute File2 but instead I'm getting this error:
Uncaught TypeError: Failed to execute 'importNode' on 'Document': parameter 1 is not of type 'Node'.
at Import.html:8
When I log the "test" variable to the console, I am getting the document that contains File1 so it's fine there. I just don't get what the error is supposed to mean and why it's not working.
When you write:
var content = document.importNode(test.content, true);
...you suppose that test is a <template> element.
So in the document you import, you should have a <template> element.
test.html:
<html>
<head>
<style>
div {
height: 300px;
width: 300px;
background-color: yellow;
}
</style>
</head>
<body>
<template><div></div></template>
</body>
</html>
In the main file, use querySelector() (or another selector function) to get the template:
var LINK = document.getElementById('LINK');
var test = LINK.import.querySelector('template');
var content = document.importNode(test.content, true);
...
I have tried hours to get the results, but failed, below, I will post all I have done, hope I can get some tips, BTW,Thanks.
from the error message, yeah It's cssRules is null, surely error!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="stylesheet" href="index.css">
<title>css style</title>
<style type="text/css">
#demo {
font-size: 10px; /*first css rule */
}
p {
border: 1px solid green; /*second one*/
}
</style>
</head>
<body>
<div id="demo" style="color: red;">
<p>how to access of document's css!</p>
</div>
</body>
external css
#demo {
font-weight: 900;
}
p {
padding: 20px;
}
Javascript
<script>
/*1,to get the inline style, this maybe the most easy one*/
var cssInline = document.getElementById('demo').style;
var cssInText = cssInline.cssText, cssColor = cssInline.color;
console.log(cssInText, cssColor);
/*2.a to get the style in head*/
var cssInHeada = document.getElementById('demo');
// using the computed css of inline
var cssHeadText = getComputedStyle(cssInHeada, null);
console.log(cssHeadText);
// 2.b to get the style directly
var cssInHeadb = document.getElementsByTagName('style')[0];
console.log(cssInHeadb.textContent);
// 2.c or like this
var cssInHeadc = document.styleSheets[1];
console.log(cssInHeadc.cssRules[0].cssText); //per rule
/*3, but I cant get the extenal style*/
var cssExtenal = document.styleSheets[0];
console.log(cssExtenal.cssRules[0].cssText);
</script>
Thank your guys!
I suspect your JavaScript is running before the stylesheet is loaded. Try this:
document.addEventListener('DOMContentLoaded', function () {
var cssExtenal = document.styleSheets[0];
console.log(cssExtenal.cssRules[0].cssText);
}, false);
Or if you happen to be using jQuery, this is more universal:
$('document').ready(function(){
var cssExtenal = document.styleSheets[0];
console.log(cssExtenal.cssRules[0].cssText);
});
Update: another possibility is that you're using Chrome and either loading the CSS cross-domain or using the file:// protocol. This appears to be a known issue and is not considered a bug.
I tried to change a background color with javascript and it didn't work out, and after lots of trying I didn't find any problem.
var x=1;
switch(x) {
case 1: {
document.getElementsByClassName("gallery").style.backgroundColor="blue";
}
}
I don't see any need to copy html or css to here. If this code is fine though I'll edit and add the other codes.
Edit: Html added, as you requested.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>פרדס מרים ומרדכי</title>
<link href="../../CSS.css" rel="stylesheet" type="text/css" />
<script language="JavaScript" src="Album1.js" type="text/javascript"></script>
</head>
<body>
<div id="wrapper">
<div id="header"></div>
<div id="menu">
<pre class="menu1"><a class="menu1" href="../../index.html">דף הבית</a> <a class="menu1" href="../../HowToArrive.html">כיצד מגיעים</a> <a class="menu1" href="../../HowItAllBegan.html">איך הכל התחיל</a> <a class="menu1" href="../../Albums.html">אלבומי תמונות</a> <a class="menu1" href="../../Contact.html">צור קשר</a></pre>
</div>
<div id="main">
<div class="gallery_bg">
<div class="gallery"></div>
</div>
</div>
</div>
</body>
</html>
Edit: CSS added. I believe you only need the part referring to the gallery class. The whole code is really long, if you need it I'll add it too, just say.
.gallery {
width:550px;
height:550px;
-webkit-background-size: 550px 550px;
-moz-background-size: 550px 550px;
background-size: 550px 550px;
border:#fff 3px solid;
margin:0 auto;
}
Try this:
document.getElementsByClassName("gallery") returns NodeList , and it is like Array , so you can do:
document.getElementsByClassName("gallery")[0].style.backgroundColor="blue";
Or do it in loop:
var galleries = document.getElementsByClassName("gallery");
var len = galleries.length;
for(var i=0 ; i<len; i++){
galleries[i].style.backgroundColor="blue";
}
"document.getElementsByClassName" work like an array if you want to change the backgrouond color you have to use loop to change the color.
Solution:
const ulList = document.getElementsByClassName("list-item");
for(var i = 0; i < liList.length; i++){
ulList[i].style.backgroundColor = 'red';
}
This question already has answers here:
How to get element's attribute set in CSS class
(6 answers)
Closed 9 years ago.
I checked it in two newest browsers.
JS code:
window.onload = function () {
alert
(document.getElementById("slideshow").getElementsByTagName ("img") [0].style.width);
}
HTML code:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Title</title>
<script src="script.js" type="text/javascript"></script>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<div id="slideshow">
<img src="slides/1.gif">
<img src="slides/2.gif">
<img src="slides/3.gif">
</div>
</body>
</html>
CSS code:
#slideshow img {
display : none;
width : 300px;
height : 200px;
}
But I have empty string from alert. Why the error (no errors in Firebug) occurs? Can I read style added from css file at all ? http://jsfiddle.net/HM47Q/
Use getComputedStyle instead.
window.onload = function () {
var elem = document.getElementById("slideshow").getElementsByTagName("img")[0];
console.log(window.getComputedStyle(elem, null).getPropertyValue("width"));
}
jsFiddle example
Empty images doesn't have width/height
Because you are retrieving inline style.
use this:
var element = document.getElementbyId("#slideshow").getElementsByTagName('img')[0];
var style = window.getComputedStyle(element),
var width = style.getPropertyValue('width');
JSFiddle: http://jsfiddle.net/enzoferber/HM47Q/2/
var firstImg = document.getElementById("slideshow").getElementsByTagName ("img") [0];
alert ( window.getComputedStyle(firstImg).width );
You have to use getComputedStyle in order to do it.
This is working::
Make your image display:block,otherwise it will return 0.
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Title</title>
<style>
#slideshow img {
display : block;
border:1px solid;
width : 300px;
height : 200px;
}
</style>
</head>
<body>
<div id="slideshow">
<img src="slides/1.gif">
<img src="slides/2.gif">
<img src="slides/3.gif">
</div>
<script>
window.onload = function () {
alert(document.getElementsByTagName('img')[0].clientWidth);
}
</script>
</body>
</html>