JS:
document.getElementsByTagName("input, select, textarea, option, optgroup, fieldset, label").onchange = function () {
var updateRate = document.querySelector('.updateRate');
updateRate.style.display = "block";
};
mark-up & styles:
<div class="updateRate">Update Rate</div>
<style>
.updateRate {
display:none;
top: 0px;
position: fixed;
width: 100%;
left: 0px;
z-index: 11111;
}
#rate, .updateRate {
background: #354563;
color: #ffffff;
cursor: pointer;
text-align: center;
padding: 10px;
}
</style>
The above is my attempt; but the banner is still not displaying after any form elements state change.
Update: So both SO answers below seem to be correct; but perhaps I didn't explain the context enough - now the 'banner' displays as soon as the form is beginning to be filled out the first time; the goal was for the banner to show after a user has gone back in and updated a form element (second time, or changing it from initial).
Context:
It is an inline quote tool; a quote will generate inline after form elements are filled out. I was trying to create a 'banner' that would que if a user has adjusted any web form element a second time. Sorry for the confusion.
You can't attach the event this way since the getElementsByTagName method accept one single tag name you could use the querySelectorAll() method instead to get the elements then loop through them using foreach and attach the event using addEventListener():
var fields = document.querySelectorAll("input, select, textarea, option, optgroup, fieldset, label");
[].forEach.call(fields, function(el) {
el.addEventListener('change', showUpdateRate, false);
});
function showUpdateRate() {
if (document.querySelector('.quote').textContent != "") {
document.querySelector('.updateRate').style.display = "block";
}
}
document.querySelector('.generate').addEventListener('click', function() {
var quote = document.querySelector('[name="type"]:checked').value;
document.querySelector('.quote').textContent = quote + " Quote";
});
.updateRate {
display: none;
top: 0px;
position: fixed;
width: 100%;
left: 0px;
z-index: 11111;
}
#rate,
.updateRate {
background: #354563;
color: #ffffff;
cursor: pointer;
text-align: center;
padding: 10px;
}
<div class="updateRate">Update Rate</div>
<br><br><br>
<form>
<input type="radio" name="type" value="Success">Success
<input type="radio" name="type" value="Motivation">Motivation
<input type="radio" name="type" value="Work">Work
<br><br>
<input type="button" class="generate" value="Generate">
</form>
<br>
<span class="quote"></span>
.getElementsByTagName() returns a node list (an array-like object), not a single element. As such, it doesn't have an onchange property to work with. After getting the node list, you'll need to loop over all the items in the list and set up the event handler for each, one at a time.
Also, .getElementsByTagName() only allows for a single tag name to be passed in, not a comma separated list. Additionally, it returns a "live node list", which has performance implications, so if you aren't dynamically adding/removing elements, you should avoid it and use .querySelectorAll() instead.
Now, option, optgroup, and label elements can only be changed via code, and don't emit or recieve a change event in the first place, so you actually don't want/need those included in your node list.
More comments about how to make your code more modern and organized inline below:
// Get this reference just once and cache it in a variable
var updateRate = document.querySelector('.updateRate');
// Gather up all the relevant elements into a node list
let elements = document.querySelectorAll("input, select, textarea, fieldset");
// Convert the node list into an Array so that .forEach()
// can safely be used to loop in all modern browsers
Array.prototype.slice.call(elements).forEach(function(element){
// Add event listeners the modern way, not with .onXyz properties
element.addEventListener("change", function () {
// Just remove the hidden class already applied to the element
// instead of working with inline styles
updateRate.classList.remove("hidden");
});
});
/*
This is applied to the "Update Rate" element in HTML by default.
It can be removed by the JavaScript when appropriate.
*/
.hidden { display:none; }
.updateRate {
top: 0px;
position: fixed;
width: 100%;
left: 0px;
z-index: 11111;
}
#rate, .updateRate {
background: #354563;
color: #ffffff;
cursor: pointer;
text-align: center;
padding: 10px;
}
<form>
<fieldset>
<legend>This is the legend</legend>
<input>
<select>
<option>choice 1</option>
<option>choice 2</option>
<option>choice 3</option>
</select>
</fieldset>
<textarea></textarea>
</form>
<!-- Set this element to be hidden by default -->
<div class="updateRate" class="hidden">Update Rate</div>
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 have toggle on the options page which should show / hide div on another page. With some help here on stack I was able to structure this code, but I can't get it to work.
Fiddle: https://jsfiddle.net/snake93/mLonrbkf/1/
I can't show / hide the divs
I don't understand how to save in localstorage
I would like to understand what values the functions of the Js code must assume. Can anyone help me?
function setSetting(name,value){
window.localStorage.setItem(name,true);
}
function getSetting(name){
return window.localStorage.getItem(name);
}
const div = document.querySelector("label-ck1");
if(getSetting("show")){
div.style.display = "block";
}
else{
div.style.display = "none";
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<label class="switch">
<input type="checkbox" id="ck1">
<span class="slider round hide-off"></span>
</label>
<br><br>
<label class="switch">
<input type="checkbox" id="ck2">
<span class="slider round hide-off"></span>
</label>
<br><br>
<div class="hideme" id="label-ck1">Please hide me...</div>
<div class="hideme" id="label-ck2">Please hide me...</div>
I copied what you wrote in your original code in jsfiddle using the same css I found there. Then I just rewrote the javascript part.
What it does is attaching a change event handler to any checkbox found on the page so that when their state it's changed, the corresponding label gets shown or hidden. The corresponding label is found using the checkbox id with the prefix label-.
In that event, the state of the checkbox changed gets pushed in the localStorage. At the very beginning, the page first checks if there's any state saved for each of the checkbox found and in case there is, its state gets updated correspondingly.
In Firefox localStorage methods (getItem/setItem) return SecurityError: The operation is insecure. I could run that code on a dedicated html page to verify it's working correctly (on Chrome for sure). So to avoid any problem at first, I added a securityFlag on top of the js code. When that flag is set to true, the localStorage won't be engaged at all so that you could see the preview here with no errors.
//if set to true, any operation involving localStorage will be excluded
let securityFlag = true;
/**
* Initialize the Checkboxes state according to what's stored on localStorage
*/
function initCheckboxStateBasedOnLocalStorage(){
//for each of the checkboxes on the page
document.querySelectorAll('input[type="checkbox"]').forEach( (checkbox, i) => {
//if securityFlag is true, skip the operation
if(securityFlag) return;
//retrieves the value stored in localStorage paired to the id passed
let valueStored = window.localStorage.getItem(checkbox.id);
//if valueStored is set
if(valueStored == 'true' || valueStored == 'false')
//sets the checkbox value with what was retrieved from localStorage
checkbox.checked = valueStored == 'true' ? true : false;
});
}
/**
* Will hide/show the label corresponding to checkbox and will save its value on localStorage
* It will be registered as the handler of the change event of every checkbox in the page
*/
function onCheckBoxStateChange(){
let checkbox = event.target;
//guesses the id of the label dedicated to the checkbox triggering the event
let msgContainer = document.getElementById(`label-${checkbox.id}`);
//if this checkbox is checked,
if (checkbox.checked){
//show the corresponding label
msgContainer.style.display = "block";
//if securityFlag is true, skip the operation
if(securityFlag) return;
//sets the state in localStorage for this.id
window.localStorage.setItem(checkbox.id ,true);
}
//otherwise
else{
//hide the corresponding label
msgContainer.style.display = "none";
//if securityFlag is true, skip the operation
if(securityFlag) return;
//sets the state in localStorage for this.id
window.localStorage.setItem(checkbox.id ,false);
}
}
//When the document has been loaded
document.addEventListener("DOMContentLoaded", function() {
//for each of the checkboxes on the page
document.querySelectorAll('input[type="checkbox"]').forEach( (checkbox, i) => {
//attach an handler to the event change
checkbox.addEventListener("change", onCheckBoxStateChange );
});
});
//reflect the value of checkboxed according to what's stored on localStorage
initCheckboxStateBasedOnLocalStorage();
.switch {
position: relative;
display: inline-block;
width: 60px;
height: 34px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: .4s;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 26px;
width: 26px;
left: 4px;
bottom: 4px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
}
input:checked + .slider {
background-color: #2196F3;
}
input:focus + .slider {
box-shadow: 0 0 1px #2196F3;
}
input:checked + .slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
/* Rounded sliders */
.slider.round {
border-radius: 34px;
}
.slider.round:before {
border-radius: 50%;
}
/*END OF TOGGLE SWITCH*/
.hideme {
padding:20px;
background: blue;
color: white;
font-weight: 800;
text-align: center;
}
<p>checkbox1</p>
<label class="switch">
<input type="checkbox" id="ck1">
<span class="slider round hide-off"></span>
</label>
<br><br>
<p>checkbox2</p>
<label class="switch">
<input type="checkbox" id="ck2">
<span class="slider round hide-off"></span>
</label>
<br><br>
<div class="hideme" id="label-ck1">Label bound to checkbox1</div>
<div class="hideme" id="label-ck2">Label bound to checkbox2</div>
In case you need a fully workable solution out of the box, I uploaded a set of files on pastebin as being:
File
Download link
demo.html
https://pastebin.com/mHWQ7564
style.css
https://pastebin.com/3jzTubFR
logic.js
https://pastebin.com/uFDrGrZx
Save them on your local computer inside the same folder and please make sure the filenames are exactly how I listed on that table. Then load the file demo.html from your web browser.
It's a slightly modified version of what I shared here. Its behavior is fully consistent with any initial condition. I also added, for the sake of further education, a strategy to add new checkbox-label pairs to the page programmatically.
There are buttons on top of the page to:
Add a new checkbox
Reset the local storage
Reload the page
There's a lot to digest there if you are not familiar with developing web pages yet. Good luck!
I need to select a css class named .slider::-webkit-slider-thumb to change a property. I have tried a multiple array of options, but JS doesn't seem to select it.
document.addEventListener("DOMContentLoaded", function (event) {
var slider = document.getElementById("importanceSlider");
var knob = document.getElementsByClassName(".slider::-webkit-slider-thumb");
knob.style.background = "#ffffff"; });
CSS:
.slidercontainer{
width: 100%;
}
.slider {
-webkit-appearance: none;
width: 100%;
height: 25px;
background: #e9ecef;
outline: none;
border-radius: 50px;
}
.slider::-webkit-slider-thumb {
-webkit-appearance: none;
width: 10%;
height: 25px;
background: #dc3545;
border-radius: 50px;
}
HTML:
<div class="slidecontainer">
<input type="range" min="0" max="10" value="5"
class="slider" id="importanceSlider" name="importance">
</div>
I want to change the width value of .slider::-webkit-slider-thumb in particular, depending on the value of the slider.
Any help would be appreciated
First, you should be aware that the pseudo-selector is non-standard...
This feature is non-standard and is not on a standards track. Do not use it on production sites facing the Web: it will not work for every user. There may also be large incompatibilities between implementations and the behavior may change in the future.
...so be wary using it.
If you are using it in a compatible browser there's a further issue:
You seem to be confusing querySelector and getElementsByClassName. The former allows you to grab elements using CSS selectors, the latter doesn't. Also, the latter returns a list of nodes.
Also, you should think about using a stylesheet rather than naming your class like that.
Here's a couple of solutions:
1) querySelector
var knob = document.querySelector(".slider");
knob.classList.add('blue');
.slider::-webkit-slider-thumb {}
.blue { background-color: blue; }
<button class="slider">Carrot</button>
2) getElementsByClassName
var knob = document.getElementsByClassName("slider");
// Grab the first element from the list
knob[0].classList.add('blue');
.slider::-webkit-slider-thumb {}
.blue { background-color: blue; }
<button class="slider">Carrot</button>
Of course if you have lots of these elements you'll need to iterate over them. You can do that with getElementsByClassName, or you can use querySelectorAll instead:
var knobs = document.querySelectorAll(".slider");
[...knobs].forEach(knob => knob.classList.add('blue'));
.slider::-webkit-slider-thumb {}
.blue { background-color: blue; }
<button class="slider">Carrot</button>
<button class="slider">Spam</button>
<button class="slider">Goat</button>
I have an <input type="text"> field and I need to clear it when this field loses focus (whiech means that user clicked somewhere on the page). But there is one exception. Input text field should't be cleared when user clicks on a specific element.
I tried to use event.relatedTarget to detect if user clicked not just somewhere but on my specific <div>.
However as you can see in snippet below, it simply doesn't work. event.relatedTarget is always returning null!
function lose_focus(event) {
if(event.relatedTarget === null) {
document.getElementById('text-field').value = '';
}
}
.special {
width: 200px;
height: 100px;
background: #ccc;
border: 1px solid #000;
margin: 25px 0;
padding: 15px;
}
.special:hover {
cursor: pointer;
}
<input id="text-field" type="text" onblur="lose_focus(event)" placeholder="Type something...">
<div class="special">Clicking here should not cause clearing!</div>
Short answer: add tabindex="0" attribute to an element that should appear in event.relatedTarget.
Explanation: event.relatedTarget contains an element that gained focus. And the problem is that your specific div can't gain a focus because browser thinks that this element is not a button/field or some kind of a control element.
Here are the elements that can gain focus by default:
<a> elements with href attribute specified
<link> elements with href attribute specified
<button> elements
<input> elements that are not hidden
<select> elements
<textarea> elements
<menuitem> elements
elements with attribue draggable
<audio> and <video> elements with controls attribute specified
So event.relatedTarget will contain above elements when onblur happens. All other elements will are not counted and clicking on them will put null in event.relatedTarget.
But it is possible to change this behaviour. You can 'mark' DOM element as element that can gain focus with tabindex attribute. Here is what standart says:
The tabindex content attribute allows authors to indicate that an element is supposed to be focusable, whether it is supposed to be reachable using sequential focus navigation and, optionally, to suggest where in the sequential focus navigation order the element appears.
So here is the corrected code snippet:
function lose_focus(event) {
if(event.relatedTarget === null) {
document.getElementById('text-field').value = '';
}
}
.special {
width: 200px;
height: 100px;
background: #ccc;
border: 1px solid #000;
margin: 25px 0;
padding: 15px;
}
.special:hover {
cursor: pointer;
}
<input id="text-field" type="text" onblur="lose_focus(event)" placeholder="Type something...">
<div tabindex="0" class="special">Clicking here should not cause clearing!</div>
I'm attempting to show an interval within a bar. Initially I was using the jQuery plugin for range, but it did not work like I wanted.
I have several different bulleted pointed within my bar. Whenever someone clicks within or near the point (in the class sliderInterval) I want the class rangeSection to be added to that area, basically showing that certain interval active. However, the rangeSection doesn't even show up, nor I am certain I am doing this correctly.
In addition, since I am doing this with intervals. I want to be able to give those intervals values, so that when one is selected I can display that value.
This is what I am trying to get it to look like:
I added a snippet to show what I have done so far. Any advise?
$(function interval() {
$(".slideInterval").click(function() {
$(this).addClass(".rangeSection");
});
});
#sliderBar {
border-radius: 15px;
width: 90%;
height: auto;
margin: 25px 10%;
background: blue;
}
.rangeSection {
width: 100px;
height: 100%;
color: purple;
position: absolute;
z-index: 1;
}
.intervalCircle {
border-radius: 50%;
height: 15px;
width: 15px;
background: red;
z-index: 1;
position: absolute;
}
.sliderInterval {
display: inline-block;
padding: 10px 8%;
}
.sliderInterval:first-child {
padding-left: 0;
}
.intervalCircle:first-child {
padding-left: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div id="sliderBar">
<div class="rangeSection"></div>
<div class="sliderInterval" onclick="interval()"><span class="intervalCircle" ></span></div>
<div class="sliderInterval" onclick="interval()"><span class="intervalCircle" ></span></div>
<div class="sliderInterval" onclick="interval()"><span class="intervalCircle"></span></div>
<div class="sliderInterval" onclick="interval()"><span class="intervalCircle"></span></div>
<div class="sliderInterval" onclick="interval()"><span class="intervalCircle"></span></div>
<div class="sliderInterval" onclick="interval()"><span class="intervalCircle"></span></div>
</div>
try this one.
You can use the .ready(); function of the jQuery library and set the .click() listener on all the .sliderInterval elements. I added the active class as well.
try it here:
https://jsfiddle.net/8cxLLts1/
$(document).ready(function(){
$(".sliderInterval").click(function() {
$(this).addClass("active");
});
});
EDIT: actually, if you use toggleClass() instead of addClass(), you'll be able to turn on and off a specific section
Using onclick in your html attribute and then binding a click event also in js could be considered redundant and unnecessary. Try removing the onclick attribute from your html and then adjust your js like so:
$(document).ready(function(){
})
.on('click', '.sliderInterval', function(){
$(this).addClass(".rangeSection");
});
Bind it to the document itself and this will help with your event delegation naturally. Also, take care to double check your class names - your js is missing the 'r' in '.sliderInterval'.