element.style.display is not what rendered in the browser - javascript

Probably a duplicate question but can not find the answer.
element.style.display is not what rendered in the browser.
Instead of returning the actual value (ie. block or inline etc), it returns empty. Tested in Chrome 56.0.2924.87 (64-bit).
How do I get the actual rendered value?
function displayStyle(aEvent) {
aEvent.target.textContent=aEvent.target.style.display;
}
window.onload = function() {
var top_array = document.getElementsByClassName("top");
for(var i = 0; i < top_array.length; i++)
{
top_array[i].addEventListener("click", displayStyle, false);
}
}
.top{
background-color:#FFF8DC;
}
<div class="top">top (click it and it will disappear because its style.display is empty)</div>

CSS styles are not available to JavaScript unless they have been formerly set in JavaScript or they have been hardcoded as inline styles.
Use getComputedStyle() instead:
function displayStyle(aEvent) {
var target = aEvent.target;
target.textContent = window.getComputedStyle(target).getPropertyValue('display');
}
window.onload = function() {
var top_array = document.getElementsByClassName("top");
for (var i = 0; i < top_array.length; i++) {
top_array[i].addEventListener("click", displayStyle, false);
}
}
.top {
background-color: #FFF8DC;
}
<div class="top">top (click it and it will now show "block" since we're getting its computed style.)</div>

Works if you set it directly in style attribute:
function displayStyle(aEvent) {
aEvent.target.textContent=aEvent.target.style.display;
}
window.onload = function() {
var top_array = document.getElementsByClassName("top");
for(var i = 0; i < top_array.length; i++)
{
top_array[i].addEventListener("click", displayStyle, false);
}
}
.top{
background-color:#FFF8DC;
}
<div class="top" style="display: block;">top (click it and it will disappear because its style.display is empty)</div>
Obs.: setting it on CSS also doesn't work.
I still don't know why, anyway.
Now I know, thanks to Rick Hitchcock's answer.

Related

How to read CSS value of pseudo class [duplicate]

I made a function that overwrite the the :hover of some elements on a page. It fades between the normal and the :hover effect. That for i had to create a .hover class in my CSS file. I think this is a little unclean. How could i read the the :hover pseudo class contents?
Using getComputedStyle as on the accepted answer won't work, because:
The computed style for the hover state is only available when the element is actually on that state.
The second parameter to getComputedStyle should be empty or a pseudo-element. It doesn't work with :hover because it's a pseudo-class.
Here is an alternative solution:
function getCssPropertyForRule(rule, prop) {
var sheets = document.styleSheets;
var slen = sheets.length;
for(var i=0; i<slen; i++) {
var rules = document.styleSheets[i].cssRules;
var rlen = rules.length;
for(var j=0; j<rlen; j++) {
if(rules[j].selectorText == rule) {
return rules[j].style[prop];
}
}
}
}
// Get the "color" value defined on a "div:hover" rule,
// and output it to the console
console.log(getCssPropertyForRule('div:hover', 'color'));
Demo
You could access document.styleSheets and look for a rule that is applied on that specific element. But that’s not any cleaner than using a simple additional class.
UPDATE: I somehow got this wrong. The below example doesn't work. See #bfavaretto's comment for an explanation.
In Firefox, Opera and Chrome or any other browser that correctly implements window.getComputedStyle is very simple. You just have to pass "hover" as the second argument:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style type="text/css">
div {
display: block;
width: 200px;
height: 200px;
background: red;
}
div:hover {
background: green;
}
</style>
</head>
<body>
<div></div>
<script type="text/javascript">
window.onload = function () {
var div = document.getElementsByTagName("div")[0];
var style = window.getComputedStyle(div, "hover");
alert(style.backgroundColor);
};
</script>
</body>
</html>
But I don't believe there's yet a solution for Internet Explorer, except for using document.styleSheets as Gumbo suggested. But there will be differences. So, having a .hover class is the best solution so far. Not unclean at all.
If there are any people here who use the questions accepted answer but it won't work, here's a nice function that might:
function getPseudoStyle(id, style) {
var all = document.getElementsByTagName("*");
for (var i=0, max=all.length; i < max; i++) {
var targetrule = "";
if (all[i].id === id) {
if(all[i].selectorText.toLowerCase()== id + ":" + style) { //example. find "a:hover" rule
targetrule=myrules[i]
}
}
return targetrule;
}
}
There is an alterantive way to get :hover pseudo class with javascript. You can write your styles of hover pseudo class in a content property.
p::before,
p::after{
content: 'background-color: blue; color:blue; font-size: 14px;';
}
then read from it via getComputedStyle() method:
console.log(getComputedStyle(document.querySelector('p'),':before').getPropertyValue('content'));

Updating array length after removing element

Disclaimer: I'm a total noob. Please bear with me ;)
I am working on a little project using CSS animations and manipulating the DOM with JS. By clicking the animated elements, they are removed from the DOM.
When all items are removed, a video element will be added.
The latter is where I am struggling with. The elements are removed from the DOM but the length of the cat array won't get updated, so it's never reaching 0 (when the video would be added).
This is an excerpt of my project:
https://codepen.io/miliberlin/pen/BaawrKg
(PS: If anybody can also help me figure out why .cat:hover {transform: rotate(360deg);} isn't working in my CSS file, I would be forever grateful)
for (i = 0; i < cat.length; i++) {
cat[i].addEventListener('click', function() {
this.parentNode.removeChild(this);
});
}
if (cat.length === 0) {
const iframe = document.createElement("iframe");
iframe.src = "https://www.youtube.com/embed/SB-qEYVdvXA";
iframe.width = "560";
iframe.height = "315";
document.getElementById("wrapper").appendChild(iframe);
}
In this case, it's better to work with HTMLCollection that is a live view on DOM instead of NodeList returned from querySeletorAll that is static.
Also, javascript isn't reactive, so code in if (cat.length === 0) {...} won't magically run when cat is empty. You have to call it yourself when this happens:
const cat = document.getElementsByClassName("cat");
for (let i = 0; i < cat.length; i++) {
cat[i].addEventListener('click', function() {
this.parentNode.removeChild(this);
if (cat.length === 0) {
whenCatEmpty()
}
});
}
function whenCatEmpty() {
const iframe = document.createElement("iframe");
iframe.src = "https://www.youtube.com/embed/SB-qEYVdvXA";
iframe.width = "560";
iframe.height = "315";
document.getElementById("wrapper").appendChild(iframe);
}
If anybody can also help me figure out why .cat:hover {transform: rotate(360deg);} isn't working
You're using transform both in the animation and in .cat:hover. CSS declarations overwrite each other. You can move cats in the animation with top property (which is a less performant than transform: translate but I don't see any other way to make it work with transform: rotate
#keyframes motion{
to{ top: 1500px }
}
In this case you should use array.splice() method
It takes 2 arguments. First - start index (i, in your case) and number of elements to delete.
You can read about it here:
https://developer.mozilla.org/ru/docs/Web/JavaScript/Reference/Global_Objects/Array/splice
for (i = 0; i < cat.length; i++) {
cat[i].addEventListener('click', function() {
this.parentNode.removeChild(this);
cat.splice(i,1); // add this to your code
});
}
if (cat.length === 0) {
const iframe = document.createElement("iframe");
iframe.src = "https://www.youtube.com/embed/SB-qEYVdvXA";
iframe.width = "560";
iframe.height = "315";
document.getElementById("wrapper").appendChild(iframe);
}
With "i" you have the index of array that you want delete, so you can use method splice for array, for example:
var array= ["hi", "everything", "ok"];
array.splice(1,1) //the first parameter is the index and the second is the number of elements that you want delete.
EDIT:
for (i = 0; i < cat.length; i++) {
cat[i].addEventListener('click', function() {
this.parentNode.removeChild(this);
cat.splice(i,1);
});
}
if (cat.length === 0) {
const iframe = document.createElement("iframe");
iframe.src = "https://www.youtube.com/embed/SB-qEYVdvXA";
iframe.width = "560";
iframe.height = "315";
document.getElementById("wrapper").appendChild(iframe);
}

How to assign new class after a button/link is clicked(JavaScript)

I'm trying to add new CSS class to HTML body but only after a button/link is clicked. I have included the code along with some errors information.
I have already created a function that can trigger an alert after the button/link is clicked. it's working perfectly but the ".classList.add " method/option is not taking any effects.
<html>
<head>
<style>
.addBGoverlay {background-color: gray;}
.bG {background-color: white;}
</style>
</head>
<body class="BG">
<a class="dash-menu mega-menu-link " role="button" href="#"><span class="mega-indicator"> navButtn</span></a>
<script type="text/javascript">
var X = document.getElementsByClassName('dash-menu');
var xLength = X.length;
var bg = document.getElementsByTagName('body');
function startAction() {
alert('This a popUp');
bg.classList.add("addBGoverlay");
}
for (var i = 0; i < xLength; i++) {
X[i].addEventListener('click', startAction, false);
}
</script>
</body>
</html>
when I inspect it in chrome it gives me an error "Uncaught TypeError: Cannot read property 'add' of undefined
at HTMLAnchorElement.startAction" and the below line is highlited in yellow color "bg.classList.add("addBGoverlay");"
getElementsByTagName() method will give you a collection of DOM elements. Since this collection don't have a classList property it will fail.
You can fix it by using the below code
bg[0].classList.add("addBGoverlay");
You can also use document.body instead of document.getElementsByTagName('body'). First one (document.body) give you the body object and second one will give you a collection object
For switch to previous state
Approach 1
function startAction() {
let body = document.body ;
if(body.classList.contans("addBGoverlay")){ // body.classList.contans() method will give a Boolean return value
body.classList.remove("addBGoverlay");
}else{
body.classList.add("addBGoverlay");
}
}
Approach 2
var hasBGOverlay = false;
var body = document.body ;
function startAction() {
if(hasBGOverlay){
body.classList.remove("addBGoverlay");
}else{
body.classList.add("addBGoverlay");
}
hasBGOverlay = !hasBGOverlay; // In each call state variable value will update with inverse value
}
Hope this helps
Try with Add ID attribute to body tag.
ie:
<body class="bg" id="dg_body">
js
var bg = document.getElementById('dg_body');
and update css order :
.bG {background-color: white;}
.addBGoverlay {background-color: gray;}
will work.
Change your code from :
bg.classList.add("addBGoverlay");
To:
bg.className += "addBGoverlay";
Hope it helps!
Enclosing it under window.load should solve it as it ensures that the DOM is ready before you try to fetch the elements.
window.onload(function() {
var X = document.getElementsByClassName('dash-menu');
var xLength = X.length;
var bg = document.getElementsByTagName('body');
function startAction() {
alert('This a popUp');
bg.classList.add("addBGoverlay");
}
for (var i = 0; i < xLength; i++) {
X[i].addEventListener('click', startAction, false);
}
});
.addBGoverlay {
background-color: gray;
}
.bG {
background-color: white;
}
<a class="dash-menu mega-menu-link " role="button" href="#"><span class="mega-indicator">navButtn</span></a>
Just change
var bg = document.getElementsByTagName('body')[0];
This will fix the error.
It must resolve your's problem.
you no need to get an element for the body. Document have that one. So
just concatenate the new class.
function startAction() {
alert('This a popUp');
document.body.className += "addBGoverlay";
}

setTimeout, JavaScript, no effect

I need to animate something, but my statement has no effect. My code:
var movei=function(img){
img.setAttribute("src", "blank.png");
}
var comp=function() {
...
for(var k=0; k<i; k++) {
var img=document.getElementById(id(k,col));
img.setAttribute("src", "circ1.png");
timer=setTimeout(movei(img),1000);
...
}
}
I read another questions about setTimeout (setInterval) but I couldn`t find an answer.
try timer=setTimeout(function(){movei(img);},1000);
If you want to animate individual elements, you should use recursion instead of loops.
Following code depicts the same:
var count = 0;
function getId(count) {
return "div_" + count;
}
function initTimer() {
setTimeout(function() {
count++;
var _id = getId(count);
var _el = document.getElementById(_id);
show(_el);
if (count < 7)
initTimer();
}, 1000);
}
function show(el) {
el.style.display = "block";
}
initTimer();
div {
display: none;
}
<div id="div_1">1</div>
<div id="div_2">2</div>
<div id="div_3">3</div>
<div id="div_4">4</div>
<div id="div_5">5</div>
<div id="div_6">6</div>
As #deceze has also written, you need to change the call to setTimeout so that you do not call the movei function, but rather pass the function 'name'. Then you would of cause have to change the behavior the function movei so it either gets a simple string name of the image or can lookup the image itself.
timer=setTimeout(movei,1000);
or (in the not recommented way)
timer=setTimeout("movei(\"imgName\")",1000);

Fade in boxes 1 after the other with jQuery

Im trying to make a 'blanket' of divs containing child divs 150px high and 150px wide.
I want each child div to fade in 1 after the other after after a millisecond or so, opacity changing from 0, to 1.
I cant seem to figure out how this works, or how id do it though?
http://jsfiddle.net/CCawh/
JS
$(function(){
var figure = [];
w = 1500;
h = 450;
for(i = 0, i < 30, i++){
$('div').append(figure[].clone()).fadeIn();
}
});
Here is a working solution.
The problems in your code
in for(i = 0, i < 30, i++), you should use ';', not ',' . Use developer tools in your browser to catch such typos
In your code $('div').append(figure[].clone()).fadeIn(); , The fadeIn applies to $('div') as append() returns the calling object itself. You must replace it with $('<figure></figure>').appendTo('div').fadeIn('slow'); and to fadeIn items one by one you could set a timeout with incrementing delays
Add display: none; style to the figure to keep it hidden initially
Here is the full code.
$(function(){
for(i = 0; i < 30; i++){
setTimeout(function(){$('<figure></figure>').appendTo('div').fadeIn('slow');}, i*200);
}
});
Here is a fiddle to see it working http://jsfiddle.net/CCawh/12/
Try using greensock TweenLite http://www.greensock.com/get-started-js/.
It has staggerTo/staggerFrom action that does exactly what you are asking. TweenLite in conjunction with jQuery makes animation very easy.
This would be a possible solution (DEMO).
Use an immediate function and call it again n times in the fadeIn callback.
$(function(){
var figure = $('figure');
var counter = 0;
(function nextFade() {
counter++;
figure.clone().appendTo('div').hide().fadeIn(500, function() {
if(counter < 30) nextFade();
});
})();
});
You can use the following implementation as an example. Using setTimeout() will do the trick.
I've updated your jsfiddle here: http://jsfiddle.net/CCawh/5/
HTML:
<div class="container">
<div class="box"></div>
</div>
CSS:
.box {
display: none;
float: left;
margin: 10px;
width: 150px;
height: 150px;
background-color: #000;
}
JS:
$(function() {
var box = $('.box');
var delay = 100;
for (i = 0; i < 30; i++) {
setTimeout(function() {
var new_box = box.clone();
$('.container').append(new_box);
new_box.fadeIn();
}, delay);
delay += 500; // Delay the next box by an extra 500ms
}
});
Note that in order for the element to actually fade in, it must be hidden in the first place, i.e. display: none; or .hide()
Here's perhaps a more robust solution without counters:
http://jsfiddle.net/CCawh/6/
for(var i = 0; i < 30; i++){
$('div').append($('<figure>figure</figure>'));
}
(function fade(figure, duration) {
if (figure)
figure.fadeIn(duration, function() { fade(figure.next(), duration); });
})($('figure').first(), 400);
By the way, clauses in for loops are separated using semicolons, not commas.

Categories