Changing text inside upload button after uploading - javascript

I have the following code:
<div className='file-upload-wrapper' data-text='Select your file!'>
<input
type='file'
id='upload'
className='file-upload-field'
placeholder='Enter Item Name'
required
onChange={(e) => setUpload(e.target.value)}
onClick={changeUploadTxt}
></input>
</div>
The CSS for this:
.file-upload-wrapper {
position: relative;
width: 400px;
height: 40px;
}
.file-upload-wrapper::after {
content: attr(data-text);
font-size: 18px;
position: absolute;
top: 0;
left: 0;
background: #fff;
padding: 10px 15px;
display: block;
width: calc(100% - 40px);
pointer-events: none;
z-index: 20;
height: 40px;
line-height: 40px;
color: #999;
border-radius: 5px 10px 10px 5px;
font-weight: 300;
}
.file-upload-wrapper::before {
content: "Upload";
position: absolute;
top: 0;
right: 0;
display: inline-block;
height: 60px;
background: #4daf7c;
color: #fff;
font-weight: 700;
z-index: 25;
font-size: 16px;
line-height: 40px;
padding: 0 15px;
text-transform: uppercase;
pointer-events: none;
border-radius: 0 5px 5px 0;
}
.file-upload-wrapper::hover {
background: darken(#4daf7c, 40%);
}
I was trying to do something with this, but it didn't worked:
const changeUploadTxt = () => {
//const txtDiv = document.getElementsByClassName("file-upload-wrapper");
};
I also changed the CSS content with before and after, but it didn't worked. Do you know a method for making sure that files are uploaded and change the text after inside the div tag?

This isn't how React works. Don't think of it in terms of directly manipulating the DOM in an event handler. Instead, think of it in terms of managing state.
The render operation displays a state value
The event updates the state value
So in the start of your component (assuming a function component, since that's the preferred approach for new development at this time) you might declare a state value:
const [uploadText, setUploadText] = useState('Select your file!');
Then you'd use that value in your rendering:
<div className='file-upload-wrapper' data-text={uploadText}>
Now all you have to do is update the state value any time you want it to change:
const changeUploadTxt = () => {
setUploadText('Some new value');
};
Any time you update state, that will trigger the component to re-render. Which is also why you don't directly manipulate the DOM in React (unless you really know what you're doing under the hood in the framework), because a re-render will break whatever changes you made to the DOM.

you can use innerHtml for adding a text.
const changeUploadTxt = () => {
document.getElementsByClassName("file-upload-wrapper").innerHTML += "Upload";
};

Related

How to set textarea height using min-height?

I am trying to create a message input field, using textarea. The reason I am using textarea is to be able to dynamically change the height.
To be able to dynamically change the height of the textarea and the parent divs, I have implemented this code.
The code works, just fine. To be able to use this JavaScript code I have to use min-height on the textarea. The problem is that I want to set the height of the textarea to 10px but it simply doesn't want to work, when using min-height. I does somehow work when I use height, but then the JavaScript won't work.
UPDATE:
I am just trying to create a field where the user can write a message and then post it.
Currently the textarea is too tall in my opinion, there is no reason for it to be taller than needed. So i want the height to initially be 20px, and then be able to expand as the user types.
UPDATE UPDATE:
I want to know how to set the height of the textarea to 10px or 20px, but still be able to dynamically change the height when the user types, using the javascript code i have provided
Any ideas on how to solve this? Btw, I'm not very well versed in CSS.
var areaName = "finder__input";
var textarea = document.getElementById(areaName);
textarea.addEventListener("input", function() {
const textarea = document.querySelector("textarea");
const textareaHeight = textarea.clientHeight;
textarea.style.height = "auto";
textarea.style.height = textarea.scrollHeight + "px";
});
body {
color: #292929;
background-color: #616f91
}
.container {
display: flex;
flex-direction: row;
justify-content: center;
position: absolute;
bottom: 0;
left: 0;
right: 0;
padding-bottom: 100px;
}
.finder {
border: 1px solid #fff;
background-color: #f6f5f0;
border-radius: 5px;
/* width: 722px; */
padding: 3px;
box-shadow: 2px 2px 1px black, -1px -1px 1px white;
}
.finder__outer {
position: relative;
/* width: 700px; */
border-radius: 5px;
min-height: 1px;
padding: 8px;
background-color: transparent;
box-shadow: inset 2px 2px 5px -2px black, inset -10px -10px 5px -7px white;
}
.finder__input {
border: none;
resize: none;
background-color: red;
outline: none;
font-size: 15px;
letter-spacing: 1px;
width: 100%;
font-weight: bold;
min-height: 10px;
max-height: 90px;
}
<div class="container">
<div class="finder">
<div class="finder__outer" id="finder__outer">
<textarea id="finder__input" class="finder__input" type="text" name="q" placeholder="Write a message..."></textarea>
</div>
</div>
</div>
Resize textarea height on input
This is basically similar to this jQuery related question: Resize Textarea on Input.
Here's a rewrite in vanilla JavaScript
const textareaResize = (elTextarea) => {
elTextarea.style.height = "auto";
const h = elTextarea.scrollHeight;
elTextarea.style.height = `${h}px`;
};
document.querySelectorAll(".flexheight").forEach((elTextarea) => {
elTextarea.addEventListener("input", textareaResize); // on input
textareaResize(elTextarea); // on init
});
textarea.flexheight {
resize: none;
width: 100%;
box-sizing: border-box;
font: inherit;
height: 1rem;
}
Starts small and increment height as user types: <br>
<textarea class="flexheight" placeholder="Write a message..."></textarea>
<br>
<textarea class="flexheight" placeholder="Write about yourself..."></textarea>
var areaName = "finder__input";
var textarea = document.getElementById(areaName);
textarea.addEventListener("input", function() {
const textarea = document.querySelector("textarea");
const textareaHeight = textarea.clientHeight;
//textarea.style.height = "10px";
textarea.style.minHeight = textarea.scrollHeight + "px";
});
try using minHeight

"Progress" bar with both value and acceptable ranges

Here is exhaustive topic on SO about how to create progress bar. I would like to improve this "widget" to display acceptable range markers. It may be vertical lines or something else.
For example, value range may be [-50;50], but acceptable range is [-25;25]. So can someone point me out how to modify, for example, the first answer from topic mentioned above to get what I described here.
Here is first suggested answer from the topic:
#progressbar {
background-color: black;
border-radius: 13px;
/* (height of inner div) / 2 + padding */
padding: 3px;
}
#progressbar>div {
background-color: orange;
width: 40%;
/* Adjust with JavaScript */
height: 20px;
border-radius: 10px;
}
<div id="progressbar">
<div></div>
</div>
Here is how I see my widget. Red parts of bar - acceptable range.
Clarification
So firstly, as mentioned in my comment, this doesn't really sound like a progress bar. As implied by the name, progress bars are meant to show progress, and so things like negative values don't make sense.
It sounds like you want something like the HTML Range Input, though you mentioned you only want to display data (which you could still technically do by setting the disabled attribute on a range input).
Possible Solution
Ultimately it looks like you just want CSS to display a range (not a progress bar). This can be achieved with pure CSS, but I should mention there are a few quirks based on the requirements you have outlined.
You could set all the values by hand, based on whatever range and value you wish to display, but I assume this isn't desirable. So the next thing to do would be to utilize CSS variables and the CSS calc() function to set everything for you (based on some initial data).
The one weird thing is displaying the text for things like the range and values. Because we are using CSS variables to hold our values and perform calculations, it would be nice to use those same values to display the text. But CSS variables cannot be converted between types and so a value of say 2 is a number (not text or a string), and this means the value of 2 cannot be displayed as text using the CSS content property. Because of this I have 2 sets of variables. The first set is the number, used for calculations to set the widths. The second set is the -text version, used to display the text under your range bar.
.rangeBar {
background: #EEE;
height: 2em;
padding: .2em;
border: 1px solid #CCC;
border-radius: 1em;
box-sizing: border-box;
position: relative;
--min-value: 0;
--min-value-text: '0';
--max-value: 4.5;
--max-value-text: '4.5';
--min-range: 1;
--min-range-text: '1';
--max-range: 3;
--max-range-text: '3';
--value: 2;
--value-text: '2';
}
.rangeBar::before {
content: var(--min-value-text);
position: absolute;
top: 100%;
left: 0;
color: #888;
}
.rangeBar::after {
content: var(--max-value-text);
position: absolute;
top: 100%;
right: 0;
color: #888;
}
.rangeBar .value {
background: #0A95FF;
width: calc(var(--value)/var(--max-value)*100%);
height: 100%;
border-radius: 1em;
position: relative;
z-index: 1;
}
.rangeBar .value::after {
content: var(--value-text);
position: absolute;
top: 100%;
right: 0;
color: #888;
}
.rangeBar .minRange {
background: #E74C3C;
width: calc(var(--min-range)/var(--max-value)*100%);
height: 100%;
border-radius: 1em;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
position: absolute;
top: 0;
left: 0;
z-index: 2;
}
.rangeBar .minRange::after {
content: var(--min-range-text);
position: absolute;
top: 100%;
right: 0;
color: #888;
}
.rangeBar .maxRange {
background: #E74C3C;
width: calc((var(--max-value) - var(--max-range))/var(--max-value)*100%);
height: 100%;
border-radius: 1em;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
position: absolute;
top: 0;
right: 0;
z-index: 2;
}
.rangeBar .maxRange::after {
content: var(--max-range-text);
position: absolute;
top: 100%;
left: 0;
color: #888;
}
<div class="rangeBar">
<div class="minRange"></div>
<div class="value"></div>
<div class="maxRange"></div>
</div>
Additional Notes
There are possibly a few ways to simplify the CSS for this and automatically take care of some of the issues with this, but would require JavaScript (which is outside of the scope of this question). There has been no indication as to how any of the data or values for this range bar will be set, and so JavaScript was avoided for this question.
EDIT
Because OP updated the original question to include JavaScript, I am adding an additional solution. This mostly works the same but instead uses a JavaScript function called _CreateRange that takes 5 parameters (min value, max value, min range, max range, and value) and creates a new element on the page that uses those parameters/values. This makes things a little simpler as you only need to enter those values once (rather than once for the number value and once for the text value) and you can also use this to dynamically create or load ranges on the page (depending on where the data for these ranges is coming from).
// These are just example values you can modify
let value = 2,
minValue = 0,
maxValue = 4.5,
minRange = 1,
maxRange = 3;
const _CreateRange = (mnV, mxV, mnR, mxR, v) => {
let r = document.createElement("div");
r.className = "rangeBar";
r.innerHTML = `<div class="minRange"></div><div class="value"></div><div class="maxRange"></div>`;
r.style.setProperty("--min-value", mnV);
r.style.setProperty("--min-value-text", JSON.stringify(mnV+""));
r.style.setProperty("--max-value", mxV);
r.style.setProperty("--max-value-text", JSON.stringify(mxV+""));
r.style.setProperty("--min-range", mnR);
r.style.setProperty("--min-range-text", JSON.stringify(mnR+""));
r.style.setProperty("--max-range", mxR);
r.style.setProperty("--max-range-text", JSON.stringify(mxR+""));
r.style.setProperty("--value", v);
r.style.setProperty("--value-text", JSON.stringify(v+""));
document.querySelector("#bar").append(r);
}
// This is where the function to create the range is called
// We are using our default example values from earlier, but you can pass in any values
_CreateRange(minValue, maxValue, minRange, maxRange, value);
.rangeBar {
background: #EEE;
height: 2em;
padding: .2em;
border: 1px solid #CCC;
box-sizing: border-box;
position: relative;
margin: 0 0 2em;
}
.rangeBar::before {
content: var(--min-value-text);
position: absolute;
top: 100%;
left: 0;
color: #888;
}
.rangeBar::after {
content: var(--max-value-text);
position: absolute;
top: 100%;
right: 0;
color: #888;
}
.rangeBar .value {
background: #0A95FF;
width: calc(var(--value)/var(--max-value)*100%);
height: 100%;
position: relative;
z-index: 1;
}
.rangeBar .value::after {
content: var(--value-text);
position: absolute;
top: 100%;
right: 0;
color: #888;
margin: .2em 0 0;
}
.rangeBar .minRange {
background: #E74C3C;
width: calc(var(--min-range)/var(--max-value)*100%);
height: 100%;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
position: absolute;
top: 0;
left: 0;
z-index: 2;
}
.rangeBar .minRange::after {
content: var(--min-range-text);
position: absolute;
top: 100%;
right: 0;
color: #888;
}
.rangeBar .maxRange {
background: #E74C3C;
width: calc((var(--max-value) - var(--max-range))/var(--max-value)*100%);
height: 100%;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
position: absolute;
top: 0;
right: 0;
z-index: 2;
}
.rangeBar .maxRange::after {
content: var(--max-range-text);
position: absolute;
top: 100%;
left: 0;
color: #888;
}
<div id="bar"></div>

Is there a way to change the value of any one button in a set of buttons at a time when one of them is clicked?

im looking for a way to change the backgroundColor of only the button which is clicked in a set of 4 buttons in react. Example:- default color is white, if the 2nd button is clicked its background color becomes blue, if the 4th button is clicked its bg color becomes blue but all others become white. I could do it with getElementBy classes/id or queryselector but the thing is it is present in 4 columns so it messes with the same button number in the other rows.
If you dont get what I mean, see the image.
export default function Quiz() {
const [quiz, setQuiz] = React.useState([]);
function getQuiz() {
fetch("my api key")
.then((res) => res.json())
.then((data) => setQuiz(data["results"]));
}
//the api gives 5 questions with 4 options each
React.useEffect(() => {
getQuiz();
}, []);
const renderQuiz = quiz.map((val) => {
// im using decodeURIComponent since its a "url base" or something type array.
let question = decodeURIComponent(val["question"]);
let correctAnswer = decodeURIComponent(val["correct_answer"]);
let wrongOptions = val["incorrect_answers"];
let allOptions = [];
wrongOptions.map((elem) => allOptions.push(decodeURIComponent(elem)));
allOptions.push(correctAnswer);
allOptions = shuffle(allOptions); //suffle is a function in another file which shuffles the contents of an array.
function RenderOptions() {
return allOptions.map((val) => {
return (
<>
<button className="opt-btn">{val}</button> {" "}
</>
);
});
}
return (
<div className="container">
<div className="question">{question}</div>
<div className="options">
<RenderOptions />
</div>
<hr className="dash" />
</div>
);
});
return <div className="quiz">{renderQuiz}</div>;
}
CSS
#import url("https://fonts.googleapis.com/css2?family=Karla&display=swap");
#import url("https://fonts.googleapis.com/css2?family=Inter&display=swap");
body {
background-color: #f5f7fb;
height: 100%;
overflow: hidden;
}
.quiz {
display: block;
margin: auto;
text-align: center;
margin-top: 30px;
width: 800px;
}
.question {
font-family: "Karla";
font-style: normal;
font-weight: bold;
font-size: 16px;
line-height: 19px;
color: #293264;
padding-top: 10px;
padding-bottom: 15px;
text-align: justify;
}
.options{
text-align: left;
max-width: 700px;
}
.opt-btn {
border: 0.794239px solid #4d5b9e;
width: -moz-fit-content;
width: fit-content;
box-sizing: border-box;
border-radius: 7.94239px;
padding-left: 15px;
padding-right: 15px;
padding-top: 5px;
padding-bottom: 5px;
background: #f5f7fb;
font-family: Inter;
font-style: normal;
font-weight: 600;
font-size: 11px;
line-height: 12px;
text-align: justify;
color: #293264;
}
.dash {
width: 630px;
height: 0px;
border: 0.794239px solid #dbdef0;
transform: rotate(-0.05deg);
margin-left: 0;
}
Will give more info if needed.

Move element created in function while manipulate existing page

I'm trying to move an existing element from oneplace in the DOM to another. I'm using a plugin for chrome "User Javascript and CSS" and the page I'm manipulate is https://www.kappahl.com/sv-SE/rea/herr/rea/?
I want to move the span element "product-card__percent--sale" below "grid-item__image-spacer". But nothing happens down in the grid with ID #Productlist. And as told, now I'm going crazy.
I know it's created in a function and I have to check if the function is done before calling the elements.
So far I have come to this point Top line is fine but not the rest of the products.
The result should be that in the bottom right corner of each image of the products would have the "50%" "sign".
I have search through every space from google to youtube but I can't understand what I'm doing wrong.
I know that just create a variabel won't work and I thought ready function would work. I also tried with length etc.
I have tried following:
let a = $(".product-card__percent--sale");
let b = $(".grid-item__image-spacer");
a.insertAfter(b);
And:
$(document).ready(function(){
if($(".product-card__percent--sale").insertAfter($("header .grid-item__image-spacer")));
});
This is how my code look like a the moment
JS
$(".product-card__wrapper").each(function () {
var newprice = $(this).find('.product-card__percent--sale');
$(this).find('.grid-item__image-spacer').parent().after(newprice );
});
CSS:
/*Override the old grid*/
#ProductList {
grid-template-columns: repeat(4, 25%);
grid-gap: 18.5px;
}
/*Create sale-percent look*/
.product-card__percent--sale {
color: white;
background-color: #ee324c;
border-radius: 50%;
height: 65px;
width: 65px;
float: right;
margin-right: 10px;
margin-bottom: 10px;
text-align: center;
/* display:inline-block;*/
font-size: 18px;
padding: 20px 5px 5px 10px;
}
It's hard to show the code when I'm just editing an existing page. Sorry for that but if someone could help me out it would just be beautiful! :)
Edit: As per the comment and the updated code in your post, the code below might fit your need.
$(function(){
setInterval(function() {
$(".product-card__percent--sale").each(function(){
$(this).closest(".product-card")
.find(".grid-item__image-spacer")
.append($(this));
});
}, 200);
})
or
$(function(){
setInterval(function() {
$(".product-card__wrapper").each(function () {
var newprice = $(this).find('.product-card__percent--sale');
$(this).find('.grid-item__image-spacer')
.parent().after(newprice );
});
}, 200);
})
both should work.
And here's the CSS
/*Override the old grid*/
#ProductList {
grid-template-columns: repeat(4, 25%);
grid-gap: 18.5px;
}
/*Create sale-percent look*/
.product-card__percent--sale {
color: white;
background-color: #ee324c;
border-radius: 50%;
height: 65px;
width: 65px;
float: right;
margin-right: 10px;
margin-bottom: 10px;
text-align: center;
/* display:inline-block;*/
font-size: 18px;
padding: 20px 5px 5px 10px;
}
.grid-item__image-spacer{
position: relative;
}
.product-card__percent--sale{
position: absolute;
bottom: 0;
right: 0;
}
You have to run the code after the dom elements are created and
let a = $(".product-card__percent--sale");
let b = $(".grid-item__image-spacer");
a.insertAfter(b);
would insert all the numbers to all the images, so it could be
setTimeout(function() {
$(".product-card__percent--sale").each(function(){
$(this).closest(".product-card__bottom--wrapper")
.find(".product-card__infoicons").prepend($(this));
});
}, 10);
or
setInterval(function() {
$(".product-card__percent--sale").each(function(){
$(this).closest(".product-card__bottom--wrapper").find(".product-card__infoicons").prepend($(this));
});
}, 200);
And you'll need these to set the position
.product-card__percent--sale{
line-height: 25px;
left: 10px;
position: relative;
}
so as you say you need to show the element below Pictures.
$(".product-card__wrapper").each(function () {
var newprice = $(this).find('.product-card__percent--sale');
$(this).find('.grid-item__image-spacer').parent().after(newprice );
});
First of all, get your all elements on your needs. then include what elements do you want to change(new price). then you can change them. I don't have the source to try it but I think it will be work fine. If it's not working try with .parent() to see what element jquery get.I hope it helps

How to add dynamic listener and get value of target

I've created a custom dropdown and would like to get the text content of the clicked element within.
Dropdown elements are created dynamically as are the event listeners but the listeners seem not to be working correctly.
Dropdown example:
I can see the listeners on each div within the dev tools.
Event listener of child div:
The first div in the dropdown fills the input with it's value but the others do not.
(function() {
let departments = ['Accounting', 'Human Resources', 'IT', 'Warehouse'];
let element = document.getElementById('dd-Department');
departments.forEach(v => {
let div = document.createElement('div');
div.appendChild(document.createTextNode(v));
div.addEventListener('click', () => {
element.parentNode.querySelector('input').value = v;
});
element.appendChild(div);
});
})();
.form-question {
display: flex;
flex-direction: column;
justify-content: center;
margin: 0 0 3rem;
min-height: 3rem;
}
.form-question__title {
color: #342357;
font-size: 1.5rem;
padding: 1rem;
}
.input-container {
border-bottom: solid 2px #333333;
position: relative;
}
input[readonly] {
cursor: pointer;
}
.input-container input {
border: none;
box-sizing: border-box;
outline: 0;
padding: .75rem;
position: relative;
width: 100%;
}
.input-container:focus-within .dropdown {
transform: scaleY(1);
}
.dropdown {
background: #ffffff;
box-shadow: 0 5px 12px #333333;
left: 0;
max-height: 300px;
overflow-y: auto;
padding: 0;
position: absolute;
right: 0;
top: calc(100% + 2px);
transform: scaleY(0);
transform-origin: top;
transition: transform .3s;
z-index: 10;
}
.dropdown div {
border-bottom: 2px solid #777777;
cursor: pointer;
padding: 8px;
z-index: 20;
}
.dropdown div:hover {
background: #dddddd;
font-weight: 800;
}
<div class="form-question">
<div class="form-question__title">
<span>Department</span>
</div>
<div class="form-question--dropdown input-container">
<input type="text" name="Department" readonly="readonly"></input>
<div id="dd-Department" class="dropdown"></div>
</div>
</div>
I also took a stab at event delegation, but could not get the text content of the clicked div. The target is the parent of the intended div, thus the text content was all child values combined.
let element = document.getElementById('dd-Department');
element.addEventListener('click', e => {
if (e.target && e.target.classList.contains('dropdown')) {
e.target.parentNode.parentNode.querySelector('input').value = e.target.textContent;
}
}, true);
Event Delegation on click of child div:
Am I missing something here?
UPDATE
Thank you #dawn for pointing out css as the problem.
I've worked around this by changing
.input-container:focus-within .dropdown
to
.input-container.active .dropdown
and adding the active class with javascript.
document.querySelectorAll('.input-container').forEach(v => {
v.onclick = () => v.classList.toggle('active');
});
Issue now is that on click of anything other than the input-container the dropdown is still active.
The following works but feels like a hack.
document.querySelectorAll('.input-container').forEach(v => {
v.addEventListener('focus', () => v.classList.add('active'), true);
v.addEventListener('blur', () => setTimeout(() => v.classList.remove('active'), 75), true);
});
Are there more elegant solutions?
This situation is a problem with css,When you click on the div,The first thing that triggers is "transform: scaleY(0)" and the ".dropdown" has invisible,so Cannot trigger click event.
Don't use input:focus-within to control the Visibilityof the drop-down box, because when you click the drop-down box, the input has lost focus.

Categories

Resources