javascript: insert large CSS string in one go, into every DOM element - javascript

This is a two part question - first I need to get every element that is a child (or subchild, etc) of a parent element, and then I need to reset it's style. As such, I'm looking to do something like the following:
var everything = parent.getEveryElementBelowThisOne();
for (i=0; i<everything.length; i++)
everything[i].css = "font: 100%/100% Verdana,Arial,Helvetica,sans-serif; color: rgb(0, 0, o); margin: 0px; padding: 0px; border-collapse: collapse; border-width: 0px; border-spacing: 0px; text-align: left; outline: 0pt none; text-transform: none; vertical-align: middle; background-color: transparent; table-layout: auto; min-width: 0px; min-height: 0px;"
So my questions are as follows:
Is there a javascript function that will effectively walk through the DOM below a given element?
Is there a javascript function that will let me set a CSS string like that? Or do I have to have a whole bunch of entries like:
everything[i].style.font = ...;
everything[i].style.color = ...;
[...]
everything[i].style.min-height: ...;
jQuery is not an option.

Instead of a string, I would use an object, much more readable and maintainable:
var new_css = {
font: '100%/100% Verdana,Arial,Helvetica,sans-serif',
color: 'rgb(0, 0, o)',
margin: '0px',
padding: '0px',
borderCollapse: 'collapse'
/* rest here ... */
}
Then use a helper function, something like:
function setStyle (element, style) {
for (var n in style) {
element[n] = style[n];
}
}
and into your for loop:
for (i=0; i<everything.length; i++) setStyle(everything[i],new_css);
A note about the setStyle function (before people downvote me for this like last time), I purposely did not use a hasOwnProperty to check the elements of style because in this case, and in most cases we are using an object not inherited from anything. If you construct your new_css object any other way or if you use a library (prototype, I'm looking at you) that modify Object's prototype which may cause problems then feel free to add the hasOwnProperty check. Anyway, setting nonexistent style values are mostly harmless. And without a hasOwnProperty check you can use inheritence to compose style objects.

Use myElement.style.cssText:
var everything = parent.getEveryElementBelowThisOne();
for (i=0; i<everything.length; i++)
everything[i].style.cssText = "font: 100%/100% Verdana,Arial,Helvetica,sans-serif; color: rgb(0, 0, o); margin: 0px; padding: 0px; border-collapse: collapse; border-width: 0px; border-spacing: 0px; text-align: left; outline: 0pt none; text-transform: none; vertical-align: middle; background-color: transparent; table-layout: auto; min-width: 0px; min-height: 0px;"
But note that this will override any inline style attributes already applied. To append extra inline css you should use:
myElement.style.cssText += '; color:red; ...'; // note the initial ";"

Its slightly offbeat, as when you talk of parent, we assume you would be considering its children at some point. But when you say, every element below this one then they may be DOM elements after the concerned element. Yours may be either of the case.
I assume you want to change style of next element siblings.
Using raw javascript, you can traverse in a generic looping way, as
nS = parent.nextElementSibling
while(nS){
nS.style.width = '100%';
// Change the desired style here
// You can also further loop on nS's children using `nS.childNodes`,
// if you want to change their styles too
nS = nS.nextElementSibling;
}
As you can see with raw javascript, the way to change styles is quite repelling.
On the other hand, jQuery gives good DOM feature.. including easy traversing, even styling.
Like, the same thing in jQuery would be.
$(parent).nextAll().each(function(){
$(this).css({'width': '100%', 'other': 'rules', 'as': 'one-dict'});
// Change the desired style here
// You can also further loop nS's children using `$(this).children()`,
// if you want to change their styles too
});
Hope this helps.

Related

JS Hover Over One Item to Make Another Move

I've got a simple text button with an image of an arrow next to it. I'm wanting the arrow image to move when someone hovers over the button.
I currently have this working in one instance with JS 'document.getElementById...', but I have several buttons across my site that I'd like to have the same behavior. My first thought would be to use a class instead of an id, and use the same functions.
For whatever reason, document.getElementsByClassName doesn't work - even in one instance.
Here's a simpler version to demonstrate - View on Codepen: https://codepen.io/sdorr/pen/JxYNpg
HTML
<HTML>
hover over me
<div id="block"></div>
hover over me
<div class="block"></div>
CSS
* {
margin: 0;
padding: 0;
}
.button {
color: #000000;
text-decoration: none;
background-color: cyan;
margin: 0;
display: block;
width: 300px;
padding: 20px;
text-align: center;
}
#block {
width: 30px;
height: 30px;
background-color: red;
}
.block {
width: 30px;
height: 30px;
background-color: green;
}
JS
function move() {
document.getElementById("block").style.marginLeft = "35px";
}
function moveBack() {
document.getElementById("block").style.marginLeft = "0px";
}
function moveAlt() {
document.getElementsByClassName("block").style.marginLeft =
"35px";
}
function moveBackAlt() {
document.getElementsByClassName("block").style.marginLeft =
"0px";
}
First off, why isn't the behavior with a class working but an id works fine?
Secondly, would a class solve this issue and be scalable across all buttons with the same two functions (onmouseover / onmouseout)?
If not, any ideas on a solution? I currently have a solution I found using jQuery that does work, but when hovering over one button, all arrow images move across the site. I don't necessarily mind this behavior because only one button is really in view at a time - but I'm trying to learn JS and solve problems with my own solutions!
I greatly appreciate your desire to learn on your own and not rely on premade solutions. Keep that spirit and you will go places!
When it comes to getElementsById, we know this should work for one element, since the function returns a single Element.
However, what does getElementsByClassName return?
(see: https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementsByClassName)
It returns an HTMLCollection which you can iterate over to change an single element's style.
So, to get this to work with JavaScript you need to write a function that will be able to identify the particular div.block you want to move. But, this puts you back to where you started, needing some particular identifier, like an id or a dataset value to pass to the function.
Alternately, based on the HTML structure you provide, you could look for nextElementSibling on the a that get's clicked. But I would set up an eventListener rather than adding a JS function as a value to the onmouseenter property.
const btns = document.getElementsByTagName('a');
/*** UPDATE forEach is a NodeList method, and will fail on HTMLCollection ***/
/* this fails -> Sorry! ~~btns.forEach(button=>{~~
/* the following will work
/**********/
for (let i = 0; i < btns.length; i++){
btns[i].addEventListener('mouseenter', function(e) {
//we pass e to the function to get the event and to be able to access this
const block = this.nextElementSibling;
block.style.marginLeft = "35px";
})
btns[i].addEventListener('mouseleave', function(e) {
const block = this.nextElementSibling;
block.style.marginLeft = "0";
})
}
But with siblings, there is a CSS-only solution.
We can use the Adjacent Sibling Selector combined with the :hover state selector and no JavaScript is needed, if we are just moving back and forth.
.button:hover+.block {
margin-left: 35px;
}
See the Snipped Below
* {
margin: 0;
padding: 0;
}
.button {
color: #000000;
text-decoration: none;
background-color: cyan;
margin: 0;
display: block;
width: 300px;
padding: 20px;
text-align: center;
}
.block {
width: 30px;
height: 30px;
background-color: green;
}
.button:hover+.block {
margin-left: 35px;
}
hover over me
<div class="block"></div>
hover over me
<div class="block"></div>
As Vecta mentioned, getElementsByClassName returns an array-like. You'll need to do something like this to get the first element:
function moveAlt() {
document.getElementsByClassName("block")[0].style.marginLeft = "35px";
}
function moveBackAlt() {
document.getElementsByClassName("block")[0].style.marginLeft = "0px";
}
However a better solution might be to use document.querySelector, which operates similarly to jQuery's $() syntax:
function moveAlt() {
document.querySelector(".block").style.marginLeft = "35px";
}
function moveBackAlt() {
document.querySelector(".block").style.marginLeft = "0px";
}

Create and Display a Div using JQuery without distorting other elements

I am trying to create a div and show a timeout message in there. But it actually distorts other parts of Page. For eg see below. Session Timed out is the div with the message.
Now I don't want this to happen. PFB the JQuery code I am using to create this Div
function ShowSessionTimeOutDiv() {
var styler = document.createElement("div");
styler.setAttribute("style","font-size:15px;width:auto;height:auto;top:50%;left:40%;color:red;");
styler.innerHTML = "<b><i>Session TimedOut, Please refresh the Page</i></b>";
document.body.appendChild(styler);
var currentDiv = $('#GoToRequestControl1_UpdatePanel1').get(0);
currentDiv.parentNode.insertBefore(styler,currentDiv) ;
}
Am I missing something here? The Part in which this div is being displayed is coming from Master Page.
Have you tried the position:fixed styling on it in css, i did that on one of my websites and it didn't distort anything.
A page has a natural flow of its elements based on the default display rules specified by the W3C. When you add a div in between other elements it naturally affects the layout of the page; the positions of the other elements.
In order to drop in a new element without it affecting other elements you have to either reserve space for it, or take it out of the normal page flow.
There are a couple of ways to take an element out of the flow — you can float it, float:left or float:right, which is great, for example, to stack blocks on the left (instead of top-down) and let them wrap to new rows as available width changes. Using a flex layout gives you a lot of control also. But in this case of one thing popping up, changing the positioning of the new element is the most straightforward and can let you put the block exactly where you want it.
I have a demonstration and full explanation in a fiddle showing several examples along the way to getting what you want.
Basically, styling is needed to reposition the timeout message element that you're inserting. Styling is better done with CSS styles, compared to adding a bunch of inline styles. If I put my timeout popup message in a "messagebox" I can make a class for it.
/* Your styles, plus a couple extra to make the example stand out better */
div.messagebox {
font-size: 16px;
width: auto;
height: auto;
top: 40%;
left: 30%;
background-color: white;
border: 2px solid black;
}
Likewise, style the message itself with a class, instead of using inline styles and the deprecated presentational tags <b> and <i>.
/* I want the message in a messagebox to be bold-italic-red text. */
div.messagebox .message {
color: red;
font-style: italic;
font-weight: bold;
}
The big difference is that we will change the positioning of the element from the default static to instead use absolute positioning:
/* I don't really recommend a class called "positioned".
A class should describe the kind of thing the element *is*
not how it *looks*
*/
div.messagebox.positioned {
position: absolute;
width: 40%;
padding: 1.5em;
}
/* The container of the positioned element also has to be positioned.
We position it "relative" but don't move it from its natural position.
*/
section#hasposition {
position: relative;
}
The term "absolute" is tricky to learn ... the element being positioned is given an absolute position within its container, in a sense it's positioned relative to its container... but what position:relative means is relative to its own natural position, so it's easy to get confused at first over whether you want absolute or relative positioning.
Putting it all together, we have some basic HTML that represents major portions of a page — a real page will have far more, but those should be contained within some top-level containers. This shows only those top-level containers.
Then we have some javascript that will add the new element at the appropriate time. Here I just call the function to add it after a delay created with setTimeout(). I'm using full-on jQuery since you're using some in your example, and it makes the javascript more portable and more concise.
function ShowSessionTimeoutStyled() {
var styler = $('<div>').addClass('messagebox').addClass('positioned');
styler.html('<span class="message">The Session Timed Out</span>');
$('#hasposition .above').after(styler);
}
// wait 6 seconds then add the new div
setTimeout(ShowSessionTimeoutStyled, 6000);
div.messagebox {
font-size: 16px;
width: auto;
height: auto;
top: 20%;
left: 20%;
background-color: white;
border: 2px solid black;
}
div.messagebox .message {
color: red;
font-style: italic;
font-weight: bold;
}
div.messagebox.positioned {
position: absolute;
width: 40%;
padding: 1.5em;
}
section#hasposition {
position: relative;
}
/* also style some of the basic parts so you can see them better in the demonstration */
section.explanation {
margin: 1em 0.5em;
padding: 0.5em;
border: 1px solid gray;
}
.demonstration {
margin-left: 1em;
padding: 1em;
background-color: #e0e0e0;
}
.demonstration .above {
background-color: #fff0f0;
}
.demonstration .middle {
background-color: #f0fff0;
}
.demonstration .below {
background-color: #f0f0ff;
}
.demonstration footer {
background-color: white;
}
p {
margin-top: 0;
padding-top: 0;
}
section {
font-family: sans-serif;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<section class="explanation">
<p>Here, a div is added dynamically, after the "basic part above", but the added div is <em>positioned</em>. You can see the other content isn't affected.</p>
<section class="demonstration" id="hasposition">
<div class="above">Basic part above</div>
<div class="middle">Middle part</div>
<div class="below">Part Below</div>
<footer>This is the page footer</footer>
</section>
</section>
I highly recommend the site Position Is Everything for articles and tutorials on positioning. Some of its other content is outdated — who needs to make PNGs to do drop-shadows any more? — but the way positioning works hasn't changed.

HTML/CSS/Javascript delete button

I have a list of items inside a div that is determined by the contents of two arrays.
product_codes=[code1, code2, code3];
quantities=[1, 34, 67,];
Every time a new code and quantity is added to its respective array, I have a javascript function that does this:
document.getElementById('cart_body').innerHTML='';
cart_text='';
elf='<br class="none"/>';
for(i=0; i<product_codes.length; i++){
cart_text+=(product_codes[i]+" (x"+quantities[i]+")"+elf);
}
document.getElementById('cart_body').innerHTML=cart_text;
and acts upon This HTML:
<div id='cart_body'></div>
with this CSS:
.none{margin-top: 0px;}
(the CSS simply overrides another styling I gave to ALL tags)
What I want to do, is at the end of each line added to cart_text (before the inserted line break), is to add a small circular button with an x in the center (I imagine that there's something like that in Bootstrap, but I am unable to use it or any other libraries) that when clicked, deletes the text next to it ON THAT LINE ONLY (the product code and quantity) from the div, AND deletes the two items(product code and quantity) from their respective arrays. Ideally, the aforementioned delete button would look something like the button that lets you delete a your comment that you've posted(here on Stack Overflow).
Please only Vanilla CSS and Javascript answers only. No libraries, please.
If it's not too much to ask, a working JsFiddle would be great too.
Thanks!
Edit
Attempt at the button: #1
#close_button{
border: 1px solid black;
padding-top: 0;
max-width: 15px;
max-height: 15px;
background-color: lightBlue;
border-radius: 90px;
font-size: 14px;
text-align: center;
}
<div id='close_button'>x</div>
This does not work because I cannot get a proper size with the x in the exact center of the circle. I tried padding, all that good stuff, but to no avail.
You can use the following code at https://jsfiddle.net/osha90/krrhvdmj/
<div id='cart_body'>
<p>
code1 x1 <span style="display:inline-block;width:30;height:30;background-color:#d2d2d2; border-radius: 50%;font-size:12px;line-height:18px;">X</span>
</p>
var product_codes=["code1", "code2", "code3"];
var quantities=[1, 34, 67,];
document.getElementById('cart_body').innerHTML='';
for(i=0; i<product_codes.length; i++){
var p = document.createElement("p");
p.appendChild(document.createTextNode(product_codes[i] +" X "+quantities[i]));
var span = document.createElement("span");
span.appendChild(document.createTextNode("X"));
span.classList.add("delete");
var a = document.createAttribute("data-productCode");
a.value = product_codes[i];
span.setAttributeNode(a);
p.appendChild(span);
span.addEventListener("click",removeElm);
document.getElementById('cart_body').appendChild(p);
}
function removeElm(){
var div = this.parentNode.parentNode;
for(i=0; i<product_codes.length; i++){
if(product_codes[i] == this.getAttribute("data-productCode"))
{product_codes.splice(i,1);
quantities.splice(i,1);
console.log(product_codes);
console.log(quantities);
break;
}
}
div.removeChild(this.parentNode);
}
css Code
.delete{
display: inline-block;
width: 19px;
height: 18px;
text-align: center;
background-color: #D2D2D2;
border-radius: 50%;
font-size: 12px;
line-height: 18px;
cursor: pointer;
}

Using class name in CSS value

I am currently doing some styling and have thought up an interesting way to do something. I want to create a piece of text that stands out among every other bit of text on the page. Below you can see the way I've done this.
var el = document.querySelectorAll('span[class^=impact]')[0],
col = el.className.split('-')[1];
el.style.textShadow = '2px 2px 0 #' + col;
html, body {
width: 100%;
height: 100%;
margin: 0;
background-image: url('http://i.imgur.com/UxB7TDq.jpg');
}
[class^=impact] {
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
font-family: Impact, sans-serif;
font-size: 72pt;
font-weight: 800;
text-transform: uppercase;
}
<span class="impact-008080">impact</span>
As you can see I'm basically getting the first half of the class and applying styles to it and grabbing the second half of the class in JavaScript and applying the shadow then. What I want to do is omit the JavaScript completely and keep it all in CSS.
I do not have a list of colours. Any and all hex colours are supported obviously. I would prefer to keep this format.
CSS attr
Theoretically, this type of thing is what the CSS attr property could be used for when browser support exists. Note that this won't work now, but when browser support does exist, it might look something like this:
HTML
<span class="impact" data-shadow="#008080">Impact</span>
CSS
.impact {
/* you text and positioning styles here */
text-shadow: 2px 2px attr(data-shadow);
}
You can read more about the attr property here:
https://developer.mozilla.org/en-US/docs/Web/CSS/attr
But for now...
Your best bet is probably to continue to use JavaScript, but instead of appending the hex code to the class name, store the hex value in a data attribute of the element, allowing you to keep the class name consistent for all instances of that element.
HTML
<span class="impact" data-shadow="#fff">Impact</span>
CSS
.impact {
/* your text and position styles here */
}
JS
var el = document.querySelector(".impact"),
shadow = el.dataset.shadow;
el.style.textShadow = '2px 2px ' + shadow;
Here's a JSFiddle for reference: http://jsfiddle.net/galengidman/xx6r1n2o/

How to automate an "onclick" if there's no element ID for that line?

In Javascript, I'm trying to automate an "onclick()", but the problem is that I don't know how to direct my "onclick()" to that element of interest.
When the element DOES have an ID though, I do this:
var redbutton = "document.getElementById("red_button")"
if (redbutton) {
redbutton.onclick();
}
But this time, after looking at the HTML of the page, my "onclick" of interest does not have an ID, so I don't know how to tell my browser to click it.
However, in the line right before it, there is an ID:
<div id="buttoncolor_pane" style="background-color: rgb(50, 50, 50); position: absolute; width: 400px; height: 200px; top: 30px; left: 0px; z-index: 998; padding-top: 25px; background-position: initial initial; background-repeat: initial initial;">
<div style="width:140px; margin:10px auto; cursor:pointer" onclick="buttoncolor_submit('blue')">
<div style="width:140px; margin:10px auto; cursor:pointer" onclick="buttoncolor_submit('yellow')">
Is there a way I can direct my code to that line, then tell it to perform the "onclick"?
You could use querySelector or querySelectorAll: (can be called on a node or document)
// get all div elements that are direct children of #buttoncolor_pane
var nodes = document.querySelectorAll('#buttoncolor_pane > div');
if (nodes === null) {
return; // or do some other error handling
}
for (var i = 0; i < nodes.length; i++) {
nodes[i].onclick();
}
You can use any query selector you like as a parameter. If you just want one, use querySelector instead. it's the same, but instead of returning a NodeList, it returns a Node, so you can just use it directly.
In this particular case, querySelectorAll will only return one element, but I think you get the idea.
Or, if you really just want the first child, then use firstChild:
document.getElementById('buttoncolor_pane').firstChild.onclick();
In jQuery, you can do something like this to trigger a click on the first child:
$('#buttoncolor_pane').children().first().click()

Categories