When using element.style = <some CSSStyleDeclaration> makes element dissapear - javascript

I use, (pls never mind the selector, I know it should be changed but it is found nevertheless).
let leftArrow = <HTMLElement> document
.querySelector('div[style="position: absolute; left: 6px; transform: skewX(22.6deg);' +
' transform-origin: 0px 0px 0px; height: 24px; width: 10px;' +
' box-shadow: rgba(0, 0, 0, 0.6) 0px 1px 6px; background-color: rgb(255, 255, 255);"]');
Then I use:
let myStyle = leftArrow.style; //this gets me a CSSStyleDeclaration object
console.log(myStyle); // those are equal objects
console.log(leftArrow.style); // those are equal objects
But when I do:
leftArrow.style = myStyle; //this should change nothing ?!
my element dissappears ?!
My goal is to use a class and then do sth like this:
leftArrow.style = this.getStyleFromClass(selector_to_find_class_name);
But after simple:
leftArrow.style = myStyle;
my element from upper one becomes :
<div></div>
which makes it basically not visible. Why ?

I think what you are trying to do is apply the original inline styles back. To do this, you can use cssText to get the values, and than you can set it back.
var div = document.getElementById("x");
var backUp = div.style.cssText;
div.style.backgroundColor = "yellow";
div.style.color = "blue";
window.setTimeout( function () {
div.style.cssText = backUp;
}, 4000);
<div id="x" style="background-color:red">Hello</div>

leftArrow.style = myStyle; //this should change nothing ?!
Yes but only if you have:
let myStyle = leftArrow.style; //this gets me a CSSStyleDeclaration object
letArrow.style = myStyle;
This is most likely not the code you have and you are generating the object on the fly. Do not assign to style directly. Instead assign to members of style e.g. letArrow.style.color = "red"

Related

Adding variables to SVG drop-shadow for-loop in Javascript

I want to add multiple "drop-shadow" filters to an SVG, to achieve a 3D style effect, with the edges being a gradient -- similar to this (I'll later add an interval to animate it). I've used this to generate a gradient array with 70 shades.
However, I cannot get the "drop-shadow" property to work with my for-loop.
Unlike "text-shadow" style for example where I can do something like this:
var current = element.style.textShadow;
var appliedStyle = -i + 'px ' + i + 'px ' + gcolor[i];
element.style.textShadow = current+appliedStyle;
I couldn't figure out how to get the current "drop-shadow" property and concatenate another filter to it. So I came up with the idea to just add classes. This is my best attempt at coding it:
let text = document.querySelector(".brand-svg");
let gcolor = ["#ffb576","#ffb275","#ffb074","#ffad73","#ffab72","#ffa871","#ffa670","#ffa370","#ffa16f","#ff9e6e","#ff9c6d","#ff996c","#ff976b","#ff946a","#ff9269","#ff9068","#ff8d67","#ff8b66","#ff8865","#ff8664","#ff8363","#ff8163","#ff7e62","#ff7c61","#ff7960","#ff775f","#ff745e","#ff725d","#ff705c","#ff6d5b","#ff6b5a","#ff6859","#ff6658","#ff6357","#ff6156","#ff5e56","#ff5c55","#ff5954","#ff5753","#ff5452","#ff5251","#ff4f50","#ff4d4f","#ff4b4e","#ff484d","#ff464c","#ff434b","#ff414a","#ff3e49","#ff3c49","#ff3948","#ff3747","#ff3446","#ff3245","#ff2f44","#ff2d43","#ff2b42","#ff2841","#ff2640","#ff233f","#ff213e","#ff1e3d","#ff1c3c","#ff193c","#ff173b","#ff143a","#ff1239","#ff0f38","#ff0d37","#ff0a36"];
for (var i = 1; i <= 70; i++) {
text.classList.add("shadowlayer"+i);
var current = document.querySelector(".shadowlayer"+i);
current.style.setProperty(`-webkit-filter`, `drop-shadow 0px (${-i})px (${gcolor[i]})`);
};
body {
display: flex;
justify-content: center;
}
.brand-svg {
width: 400px;
}
<svg class="brand-svg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 748.17 433.89"><defs>
<style> .brand-svg path, polygon {fill:#fff;stroke:#ffb777;stroke-miterlimit:10;stroke-width:3px;}</style>
</defs>
<path class="svg-b-d" d="M746.37,26.21q-1.07-10.31-13.43-17.09T701.85,1.74c-1,0-2,0-3,0a142.7,142.7,0,0,0-37.5,5.51,200.39,200.39,0,0,0-43.71,18L559.68,57l86.6,47.6,57.94-31.84q21.79-12,32.51-24.13T746.37,26.21ZM694.43,46q-3.63,7.51-18.36,15.6l-10.41,5.73L627,46.1l10.4-5.72q14.73-8.1,28.39-10.09a51.29,51.29,0,0,1,7.33-.56,32,32,0,0,1,15.63,3.68Q698,38.51,694.43,46Z"/>
<polygon class="svg-b-n" points="539.34 112.27 583.62 136.62 493.18 137.64 449.59 161.6 536.18 209.2 579.78 185.24 536.24 161.31 625.93 159.87 669.53 135.91 582.93 88.31 539.34 112.27"/>
<path class="svg-b-a" d="M308.71,209.12l35.73,75.55,45.77-25.15-6.22-11,46.35-25.47,20,3.42,45.95-25.26L359,181.48Zm90.59,8.56-25.13,13.81-11.53-20.15Z"/>
<path class="svg-b-r" d="M326.64,290.81q1-8-7.66-12.75-8.07-4.43-20.48-4.66l-1.27,0q-11.94,0-26.85,4.31a160.09,160.09,0,0,0-32.6,13.82L173,327.12l86.6,47.61,43.6-24-30.88-17.18,57.2,2.72L377.4,310,314,308.37Q325.61,298.76,326.64,290.81Zm-47.71,17.27c-.91,1.81-3.46,3.85-7.65,6.16L254,323.74l-14.68-8.07,17.28-9.5a33.36,33.36,0,0,1,11.28-4.06,21.59,21.59,0,0,1,2.7-.18,12.06,12.06,0,0,1,5.93,1.37Q280.29,305.39,278.93,308.08Z"/>
<path class="svg-b-b" d="M186,353.17q-6.37-3.5-16.52-3.5c-1.16,0-2.37,0-3.61.1a84.43,84.43,0,0,0-27.06,6.61q8.9-6.78,10.07-13.14t-6.4-10.53a34,34,0,0,0-16.65-3.82,63.66,63.66,0,0,0-12.28,1.3q-17.93,3.53-39.14,15.19l-70.89,39L90.13,432,163,391.9q22.57-12.4,28.49-22.45T186,353.17ZM80.89,378.94l-12.24-6.73,16.9-9.28a33.73,33.73,0,0,1,10.27-4,15,15,0,0,1,2.45-.21,10.35,10.35,0,0,1,5.1,1.24c2,1.12,2.6,2.46,1.68,4.05s-3.35,3.45-7.27,5.61Zm58.23.6q-1.44,2.34-7.33,5.58L112,396,99.72,389.3l19.84-10.91q7.53-4.14,12.81-4.14a10.27,10.27,0,0,1,5,1.2C139.5,376.62,140.09,378,139.12,379.54Z"/></svg>
Why doesn't this work? Is there an easier way to do this?
The filter property accepts multiple filters, so you can repeat drop-shadow:
filter: drop-shadow(30px 10px 4px #44d) drop-shadow(70px 1px 4px #f0d);
All you need to do is pass correct string to style property filter.
In your case code would look something like this:
let text = document.querySelector(".brand-svg");
let gcolor = ["#ffb576","#ffb275","#ffb074"];
for (var i = 0; i < gcolor.length; i++) {
text.style.filter = `${text.style.filter} drop-shadow(0px ${-i}px 0px ${gcolor[i]})`;
}

Invalid characters in HTML preventing my menu from toggling?

I'm creating a button that makes a table and a drop down menu. But the drop down menu doesn't work when it's clicked, like it should.
I keep receiving this error: 'Uncaught InvalidCharacterError: Failed to execute 'toggle' on 'DOMTokenList': The token provided ('[object HTMLCollection]') contains HTML space characters, which are not valid in tokens.' This error occurs near the last line here where I use toggle.
I've checked the HTML generated with JS and it seems fine. I just can't figure out what exactly this error is referring to. I've gone through the code at least a half dozen times and don't see any spaces within the html. This makes me question whether this is where the problem actually is. If anyone has a solution or insight into how to correct this, that would be much appreciated.
Here is the code:
//**HTML**
<div class = 'insertsGrid buttonStyle'>Grid</div>
//**CSS**
.buttonStyle{
width: 12vh;
height: 12vh;
border: 1px solid;
position: fixed;
display:flex;
justify-content:center;
align-items: center;
font-weight: 900;
font-size: 4vh;
color: #b8860b;
cursor:pointer;
background-color:white;
user-select: none;
}
th, td, tr {
border: 1px solid black;
}
//**Javascript**
const insertsGrid = document.getElementsByClassName('insertsGrid');
insertsGrid[0].addEventListener('mousedown', createGrid);
let z =0;
function createGrid (){
z++;
document.execCommand('insertHTML', true,/*this is the bar */'<div class=bar'+z.toString()+'></div>'+/*this is the menu options that show when bar is clicked */ '<div class =dropDownContent'+z.toString()+'><div class =dropDownContentX'+z.toString()+'><p>Add Row</p><p class=addColumn'+z.toString()+'>Add Column</p></div></div>'+/*this is the table */ '<table><tr><td>Head1</td><td>Head2</td></tr><tr><td></td><td></td></tr></table><br>');
let bar = document.getElementsByClassName('bar'+z.toString());
let dropDownContent = document.getElementsByClassName('dropDownContent'+z.toString());
let dropDownContentX = document.getElementsByClassName('dropDownContentX'+z.toString());
let addColumn = document.getElementsByClassName('addColumn'+z.toString());
//dom css for the html created in the dom
bar[0].style.width = '10%';
bar[0].style.height = '1%';
bar[0].style.border = '1px solid black';
bar[0].style.cursor = 'pointer';
bar[0].style.marginBottom = '50px';
dropDownContent[0].style.display = 'none';
dropDownContentX[0].style.display = 'inline';
dropDownContentX[0].style.backgroundColor = 'white';
dropDownContentX[0].style.width = '100%';
dropDownContentX[0].style.fontSize = '80%';
//action executed when the nav button is pressed
bar[0].addEventListener('mousedown' , tog);
function tog (){
dropDownContent[0].classList.toggle('dropDownContentX');
}
You are passing dropDownContentX into the toggle() method. This is a collection of DOM nodes, not a string. The toggle() method expects a CSS class name without spaces. You are passing the wrong data type to this method.
Source: https://developer.mozilla.org/en-US/docs/Web/API/DOMTokenList/toggle

How to change variables within the same function. Javascript

I want to change an input value by clicking on a div + adding a CSS effect . The issue is that I got 10 divs. I can do it by making a function for each one of them but i´m pretty sure that there is a much better way to do it without repeating the code. I´m starting at this, so it would be great if i could get some help.
I got an input and a few divs with just numbers:
<input type="text" id="screen" dir="rtl"/>
<div class="key" id="tres">3</div>
<div class="key" id="dos">2</div>
<div class="key" id="uno">1</div>
...
And then the function:
var screen = document.getElementById("screen");
var tres = document.getElementById("tres");
var dos = document.getElementById("dos");
var uno = document.getElementById("uno");
uno.onclick=function to () {
screen.value+=uno.innerHTML;
uno.style.boxShadow="none";
setTimeout(function() {
uno.style.boxShadow="0px 0px 5px 2px rgba(0, 0, 0, 0.2), inset 0px 0px 1px 1px rgba(0, 0, 0, 0.2)";
}, 220);
}
Now what i want to do is to use the same ".onclick" function on every single div, not in my newbie way (re writting the function and changing variables) but in a practical one. I've been trying with "classname" instead of "id". I´ve attempted with ".replace" by trying to replace the value names. And actually im trying with something like this:
var hell=["uno","dos","tres"];
var screen = document.getElementById("screen");
for (var i = 0; i < hell.length; i++) {
document.getElementById(hell[i]).onclick=function to() {
screen.value+=document.getElementById(hell[i]).innerHTML;
}
}
I barely know what I´m doing so a little bit of enlightenment would be grateful (avoiding jQuery if possible).
Thanks a lot! (Sorry about my bad English)
You can simply select the elements by class name, attach an eventListener to each of the elements, and you'll get what you wanted.
var screen = document.getElementById("screen");
var elements = Array.from(document.getElementsByClassName('key'));
elements.forEach(el => el.addEventListener('click', fn));
function fn(e) {
screen.value = (parseInt(screen.value, 10) || 0) + parseInt(e.target.innerText, 10);
elements.forEach(el => el.style.boxShadow = "none");
setTimeout(function() {
e.target.style.boxShadow = "0px 0px 5px 2px rgba(0, 0, 0, 0.2), inset 0px 0px 1px 1px rgba(0, 0, 0, 0.2)";
}, 220);
}
<input type="text" id="screen" dir="rtl" />
<div class="key" id="tres">3</div>
<div class="key" id="dos">2</div>
<div class="key" id="uno">1</div>
I think what you're looking for is querySelectorAll. Basically, instead of getting each one by ID, you can get all of them in an array by getting them by their class (since they all have the same class).
var myInput = document.getElementById('screen');
var inputs = document.querySelectorAll('.key');
inputs.forEach(function(input) {
input.onclick = function() {
myInput.value += input.innerHTML;
// everything else goes here
}
});
See this pen for a working example.
ES6
Here is a more optimal solution to your problem. It may seem a bit obscure if you don't know new ES6 syntax, but since you want to learn anyway, here it goes:
let screenValue = 0,
lastClickedDiv;
const screen = document.getElementById('screen'),
keys = [...document.getElementsByClassName('key')];
const toggleBoxShadow = (clickedDiv) => {
if (clickedDiv !== lastClickedDiv) {
clickedDiv.classList.add('active');
if (lastClickedDiv !== undefined) {
lastClickedDiv.classList.remove('active');
}
lastClickedDiv = clickedDiv;
}
};
const keysClickListener = (event) => {
screenValue += parseInt(event.target.textContent);
screen.value = screenValue;
setTimeout(() => toggleBoxShadow(event.target), 220);
};
keys.forEach(key => key.addEventListener('click', keysClickListener));
.key.active {
box-shadow: 0 0 5px 2px rgba(0, 0, 0, 0.2),
inset 0 0 1px 1px rgba(0, 0, 0, 0.2);
}
<input type="text" id="screen" dir="rtl"/>
<div class="key">3</div>
<div class="key">2</div>
<div class="key">1</div>
[...document.getElementsByClassName('key')] gets all elements with class name key and turns that collection into an array using spread operator.
keys.forEach(key => key.addEventListener('click', keysClickListener)) adds click listener named keysClickListener to each element of keys array.
keysClickListener function updates screenValue counter by adding number from clicked div to value it already stores and sets that as #screen's value. Then it invokes toggleBoxShadow() after 220 ms.
If you want anything else explained, let me know.

Javascript transition duration won't work

I wan't to make show() and hide() method like jquery have, but with pure javascript because I want to modify how the element show and hide. But after my attempts, I've changed where the code placed, changed the code, etc, still it won't work. Only for a few times it was work but it was inconsistent (when I try to run it through firefox, it work for once but never again). The display (block and none) and the exact width and height is work, but not the 2s transition. How to fix this?
<!doctype html>
<html>
<head>
<style>
div {
width: 0px;
height: 0px;
background-color: yellow;
border: 1px solid black;
display: none;
}
</style>
</head>
<body>
<div>
</div>
<button>
click !
</button>
<script>
var x = document.getElementsByTagName("button");
var y = document.getElementsByTagName("div");
x[0].onclick = fungsi;
function fungsi() {
if (y[0].style.display != "block") {
y[0].style.display = "block";
y[0].style.transition = "width 2s";
y[0].style.transition = "height 2s";
y[0].style.width = "100px";
y[0].style.height = "100px";
} else {
y[0].style.display = "";
y[0].style.transition = "width 2s";
y[0].style.transition = "height 2s";
y[0].style.width = "";
y[0].style.height = "";
}
};
</script>
</body>
</html>
I've messed around with your code and found that the reason for your problem was the switching from display: none; to display: block; and back. I've made a simple solution for this if you would like to use it.
Here is the modified CSS code.
div {
width: 0px;
height: 0px;
background-color: yellow;
border: none;
}
Here is the modified JS code.
var x = document.getElementsByTagName("button");
var y = document.getElementsByTagName("div");
x[0].onclick = fungsi;
var expanded = false;
function fungsi() {
y[0].style.transition = "all 2s";
if (!expanded) {
y[0].style.border = "1px solid black";
y[0].style.width = "100px";
y[0].style.height = "100px";
expanded = true;
} else {
var applyBorder = function () {
y[0].style.border = "none";
};
y[0].style.width = "0";
y[0].style.height = "0";
expanded = false;
setTimeout(applyBorder, 2000);
}
};
And here is a JSFiddle of this code for an example.
It could be something to do with vendor prefixes
So as well as having the following:
y[0].style.transition
You will also need:
y[0].style.mozTransition
y[0].style.webkitTransition
Try that, hopefully should work.
What you are doing is not animating show and hide with "pure javascript", it really is animating show and hide with CSS3. You are just setting the CSS properties trough javascript!
That being said, CSS3 transitions are not supported by every browser. For example even IE9 does not support it. Some other browser only work with prefixed versions of this property.
Try setting -moz-transition, -webkit-transition and -o-transition too.
For more details see: Transition browser support
However if you expect your animation to work across all major platforms I suggest you to use jQuery and try adjusting the settings to your desired behavior..
When you run
y[0].style.transition = "width 2s";
y[0].style.transition = "height 2s";
The first line will be overwritten by the second. So the transition goes only for width or height at a time, and when one of them is 0, the transition will be invisible.
You should set transition like this:
y[0].style.transition = 'width 2s,height 2s';
Or just set it for all properties that support transition:
y[0].style.transition = 'all 2s';
BTW, since the transition property is not changing, you should set them outside the changing part.
Another problem is, the <div> must be visible before the animation starts, otherwise it will have the desired width and height once become visible, and no transition is needed any more. visibility is another choice since an element with visibility: hidden still takes the place.
Here is a working copy of code:
<!doctype html>
<html>
<head>
<style>
div {
width: 0px;
height: 0px;
background-color: yellow;
border: 1px solid black;
/* Use visibility instead of display */
visibility: hidden;
}
</style>
</head>
<body>
<div>
</div>
<button>
click !
</button>
<script>
var x = document.getElementsByTagName("button");
var y = document.getElementsByTagName("div");
x[0].onclick = fungsi;
// set transition
y[0].style.transition = "width 2s,height 2s";
function fungsi() {
if (y[0].style.visibility != "visible") {
y[0].style.visibility = "visible";
y[0].style.width = "100px";
y[0].style.height = "100px";
} else {
y[0].style.visibility = "";
y[0].style.width = "";
y[0].style.height = "";
}
};
</script>
</body>
</html>
And something more, if you want to hide the element after the animation ends, setTimeout will work.

Cannot update div style attributes

I'm trying to change a couple of style attributes. Here is the style I first assigned to clickedMenuHexagonDiv:
clickedMenuHexagonDiv.style.MozTransform = "scale(.9999)";
clickedMenuHexagonDiv.style.width = num2px(this.width);
clickedMenuHexagonDiv.style.marginRight = "3px";
clickedMenuHexagonDiv.style.position = "relative";
clickedMenuHexagonDiv.style.zIndex = "5";
clickedMenuHexagonDiv.style.styleFloat = "right";
clickedMenuHexagonDiv.style.cssFloat = "right";
Here is the code later on, for the attributes I want to change:
clickedMenuHexagonDiv.className = "centerHexagonDiv";
clickedMenuHexagonDiv.style.bottom = "auto";
clickedMenuHexagonDiv.style.right = "auto";
clickedMenuHexagonDiv.style.float = "none";
alert("clickedMenuHexagonDiv: " + clickedMenuHexagonDiv.style.cssText);
And here is the output of the alert:
clickedMenuHexagonDiv: transform: scale(0.9999); width: 80px; margin-right: 3px; position: relative; z-index: 5; float: right; right: auto; bottom: auto;
As you can see, it only updates attributes that weren't declared earlier. There were no console errors. Also, somewhat oddly, bottom and right were changed by the jquery animate function (I put an alert in before clickedMenuHexagonDiv.className = "centerHexagonDiv";, and it showed values other than "auto" for bottom and right.
I figured out that I can sort of fix this if I insert clickedMenuHexagonDiv.style.cssText = "" before changing the style attributes, but then I have to declare all of the attributes again. I'd rather try to understand why I can't update specific attributes.
If figured it out: I needed to use "styleFloat" and/or "cssFloat". "float" isn't a css property.
Here's the updated code (only the second section needed updating):
clickedMenuHexagonDiv.className = "centerHexagonDiv";
clickedMenuHexagonDiv.style.bottom = "auto";
clickedMenuHexagonDiv.style.right = "auto";
clickedMenuHexagonDiv.style.styleFloat = "none";
clickedMenuHexagonDiv.style.cssFloat = "none";

Categories