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;
}
Related
After searching a lot on Stackoverflow, I couldn't find a solution where only Javascript used code was achieving to do the task that I wanted to create.
I have a form created on React where I am generating input fields with the help of add and remove buttons. On the other hand, what I want is based on the user input on the field, there will be other inputs as well. To clarify more let's take a look at the example picture below to draw the frontend profile:
Front end of the Webpage
When the user enters the quantity of products, new fields will be automatically generated based on the input value without the need for clicking any button. For example if the quantity is 5, I need 5 input fields for that product as in the image below
Dynamic Input Fields
I want to achieve this using Javascript functions but since I am a beginner, I don't know what to use or apply. I would appreciate a lot for your advises and solutions. Cheers!
My answer is not strictly a component but it shows more or less how you can deal with a confined structure (package) capturing the input event listener for all the quantity input text controls.
Every time the event fires (when the user inputs data inside the field), the given number of "products" are created and added to the corresponding element in the package where the event originated.
Styling wise it's terrible but I did the bare minimum to deliver something worth seeing.
I styled the product number in the list using css counters and a ::before pseudo element just for the sake of adding maybe useful ideas to the game:
//add input event listener to qty input
[...document.querySelectorAll('.package .qty')]
.forEach( qtyEl => {
qtyEl.addEventListener('input', (event)=>{
const qtyEl = event.target;
const qty = event.target.value;
clearProducts(qtyEl);
addProducts(qtyEl, qty);
});
})
//removes the products in the given package
function clearProducts(from){
const target = from.closest('.package').querySelector('.products');
target.innerHTML = '';
}
//adds a number of products in the given package
function addProducts(from, n){
const target = from.closest('.package').querySelector('.products');
for(let i = 0; i<n; i++){
const product = createProduct();
target.append(product);
}
}
//creates and returns a product
function createProduct(){
const product = document.createElement('div');
const input = document.createElement('input');
product.classList.add('product');
input.classList.add('product-name');
product.append(input);
return product;
}
.package{
display: flex;
flex-wrap: wrap;
gap: 1em;
border: solid purple;
padding: 1em;
margin-bottom: 1em;
counter-reset: product;
}
.package .type{
width: 50%;
height: 2rem;
}
.package .products{
width: 100%;
display: flex;
flex-direction: column;
padding: 1em;
}
.product{
position: relative;
margin-bottom: 1em;
border: solid 1px darkgray;
padding: 1em;
}
.product::before {
counter-increment: product;
position: absolute;
content: "Product " counter(product) ": ";
top: -13px;
left: 10px;
font-size: 1rem;
color: darkgray;
background: white;
padding: 2px;
}
.product input{
width: 100%;
border: none;
}
<div class="package">
<input type="text" class="type">
<input type="number" class="qty">
<div class="products">
</div>
</div>
<div class="package">
<input type="text" class="type">
<input type="number" class="qty">
<div class="products">
</div>
</div>
I am working with a CMS. In order to show some list content I need a count on the list items to spread them evenly over the page. I did this function:
$(document).ready(function() {
$("button").click(function() {
var count = $("#page_menu ul li").length;
var breit = 945 / count;
$("#page_menu li").css("width", breit);
})
})
#page_menu ul {
margin: 0px;
top: 0px;
}
#page_menu li {
border: 1px solid #fff;
margin: 0 -1px 0 0;
display: flex;
justify-content: center;
text-align: center;
align-items: center;
}
#page_menu ul li {
height: 50px;
float: left;
list-style: none;
font-size: 13px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="page_menu">
<ul class="nav menu mod-list">
<li class="item-121"><a href="/index.php/about/querfloete" >Querflöte</li><li class="item-123"><a href="/index.php/about/autor" >Autor</li></ul>
</div>
<button>change</button>
This does exactly what it is supposed to do - TOO LATE!
To be precise: The page loads and builds the list. The javascript afterwards performs its counting function and releases it to the CSS. However, by then the CSS relating to the list doesn't get the information anymore. The button stays blank and without function regardless if it was placed before the javascript section or after. And it stays that way until I go a level down in the submenu with the same menu. Then it works fine.
And the other thing is, that I would like to load it by itself and not via clicking a button. How can I achieve this?
Sorry, I haven't been working this kind of stuff for the past 10 yrs. I am completely somewhat lost.
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";
}
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.
I have a wrapper div and many content blocks. The content block can be of any number.
<div class="wrapper">
<div class="content-block">Something goes here</div>
<div class="content-block">Something goes here</div>
.
.
.
<div class="content-block">Something goes here</div>
</div>
I wish to form a pyramid structure using these content-blocks as it appears below:
Is it possible to achieve pyramid like this? The above image is just an example, there can be more than 10 content-blocks or even less.
Check out this very simple JavaScript/CSS solution:
var objContainer = document.getElementById("container"),
intLevels = 10,
strBlocksHTML = '';
// Using innerHTML is faster than DOM appendChild
for (var i = 0; i < intLevels; i++) {
for (var n = 0; n < i + 1; n++) {
strBlocksHTML += '<div class="buildingBlock"></div>';
}
strBlocksHTML += '<div></div>'; // Line break after each row
}
objContainer.innerHTML = strBlocksHTML;
.buildingBlock {
display: inline-block;
width: 20px;
height: 20px;
margin: 2px 5px;
background-color: #eee;
border: 2px solid #ccc;
}
#container {
text-align: center;
}
<div id="container"></div>
Yes, it is perfectly possible, but hard to write down without more precise requirements. Number of divs would obviously equal number of elements = 10. Length of bottom row = (10/2 - 1) with each next row to top taking one less element, etc. Either use absolute positioning in div style or treat table as matrix and draw with cells. Table solution will be progressively slower with more rows, because all the empty "pixels" and quadratically increasing overhead on recalculating cell sizes and positions in browser.
Hm, not a trivial task. I don't think it is possible to write (finite) CSS for any number of elements. It would need something like this:
#wrapper {
text-align: center;
}
.content-block {
display: inline-block;
width: 5em;
height: 4em;
margin: 0 2.5em;
}
.content-block:nth-child(n*(n+1)/2)::after {
display: block; /* linebreak */
}
Where the nth-child-selector would contain a triangular number, but it must have the form an+b.