I'm working on a Javascript RGB Color Picker and struggling with the front-end.
A RGB Color Picker results in a color that exists within the combination of three values (R,G,B), with each value having a range of 0 - 255.
This is my project so far, it allows an user to pick a combination from a range selection but it doesn't showcase the value that he picked.
<body>
<div class="picker">
Red <input type="range" min="0" max="255" step="1" id="red" value="115">
Green <input type="range" min="0" max="255" step="1" id="green" value="10">
Blue <input type="range" min="0" max="255" step="1" id="blue" value="162">
<div id="display"></div>
</div>
<script type="text/javascript">
var input = document.querySelectorAll("input");
for (var i = 0; i < input.length; i++) {
input[i].addEventListener("input",
function () {
var
red = document.getElementById("red").value,
green = document.getElementById("green").value,
blue = document.getElementById("blue").value;
var display = document.getElementById("display");
display.style.background = "rgb(" + red + ", " + green + ", " + blue + ")";
}
);
}
</script>
</body>
How do I showcase the selected value from the range selection so that the user knows the combination he is selecting?
I'm new to JS, HTML and CSS so I have no idea what I'm supposed to do.
By default, a div's size is 0 by 0. You just need to add some style to the display to give it some size, then the background will be visible:
<body>
<div class="picker">
Red <input type="range" min="0" max="255" step="1" id="red" value="115">
Green <input type="range" min="0" max="255" step="1" id="green" value="10">
Blue <input type="range" min="0" max="255" step="1" id="blue" value="162">
<div id="display" style="height: 50px; width: 50px;"></div>
</div>
<script type="text/javascript">
var input = document.querySelectorAll("input");
for (var i = 0; i < input.length; i++) {
input[i].addEventListener("input",
function () {
var
red = document.getElementById("red").value,
green = document.getElementById("green").value,
blue = document.getElementById("blue").value;
var display = document.getElementById("display");
display.style.background = "rgb(" + red + ", " + green + ", " + blue + ")";
}
);
}
</script>
</body>
Related
I am trying to build a JavaScript colour picker on my website. It currently works correctly, but I want it to also write the hex value. The hex converter also works fine, but I think I'm having trouble with the actual converting parameters. Take a look at this.
function componentToHex(c) {
var hex = c.toString(16);
return hex.length == 1 ? "0" + hex : hex;
}
function rgbToHex(r, g, b) {
return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
}
var input = document.querySelectorAll("input");
for (var i = 0; i < input.length; i++) {
input[i].addEventListener("input", function() {
var red = document.getElementById("red").value,
green = document.getElementById("green").value,
blue = document.getElementById("blue").value;
var display = document.getElementById("display");
display.style.background = "rgb(" + red + ", " + green + ", " + blue + ")";
document.write(rgbToHex(red + ", " + green + ", " + blue));
});
}
<div id="tools">
<div class="picker">
<P>Red</P> <input type="range" min="0" max="255" step="1" id="red" value="115">
<p>Green</p> <input type="range" min="0" max="255" step="1" id="green" value="10">
<p>Blue</p> <input type="range" min="0" max="255" step="1" id="blue" value="162">
<div id="display"></div>
</div>
It should actually write the hex value to the document, but that does not seem to be working.
Thanks,
Rat (Joey)
Try this if it helps:
function componentToHex(c) {
var hex = c.toString(16);
return hex.length == 1 ? "0" + hex : hex;
}
function rgbToHex(r, g, b) {
return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b);
}
var input = document.querySelectorAll("input");
for (var i = 0; i < input.length; i++) {
input[i].addEventListener("input", function() {
var red = document.getElementById("red").value,
green = document.getElementById("green").value,
blue = document.getElementById("blue").value;
var display = document.getElementById("display");
display.style.color = "rgb(" + red + ", " + green + ", " + blue + ")";
display.innerHTML = rgbToHex(red , green , blue);
});
}
<div id="tools">
<div class="picker">
<P>Red</P> <input type="range" min="0" max="255" step="1" id="red" value="115">
<p>Green</p> <input type="range" min="0" max="255" step="1" id="green" value="10">
<p>Blue</p> <input type="range" min="0" max="255" step="1" id="blue" value="162">
<div id="display" style = "width:50px; height:50px"></div>
</div>
So I'm trying to make an html page with 3 sliders for the background (R,G,B)
I want each slider to call a function that changes the background on input, but I can't get it to work unless I rewrite the function for each event. Please bear with me, I'm a new coder and most of this is spaghetti
var red = document.getElementById("red"); //slider values
var routput = document.getElementById("reddemo"); //display values
routput.innerHTML = red.value; //for function
var green = document.getElementById("green");
var goutput = document.getElementById("greendemo");
goutput.innerHTML = green.value;
var blue = document.getElementById("blue");
var boutput = document.getElementById("bluedemo");
boutput.innerHTML = blue.value;
red.oninput && blue.oninput && green.oninput = function() {
//this is what I want to do; string all of these oninputs together... obviously this way doesn't work
var x = Math.round([Number(red.value) + Number(blue.value) + Number(green.value)] / 3);
//this is for later, the text color in body changes to white when color values get too low
routput.innerHTML = Math.round(this.value);
boutput.innerHTML = Math.round(this.value);
goutput.innerHTML = Math.round(this.value);
document.body.style.backgroundColor = "rgb(" + routput.innerHTML + "," + routput.innerHTML + "," + routput.innerHTML + ")";
if (x < 127.5) {
document.body.style.color = "rgb(255,255,255)";
} else {
document.body.style.color = "rgb(0,0,0)";
}
}
<p>Red</p>
<div class="slidecontainer">
<input type="range" min="0" max="255" value="255" class="slider" id="red">
<p>Value: <span id="reddemo"></span></p>
</div>
<p>Green</p>
<div class="slidecontainer">
<input type="range" min="0" max="255" value="255" class="slider" id="green">
<p>Value: <span id="greendemo"></span></p>
</div>
<p>Blue</p>
<div class="slidecontainer">
<input type="range" min="0" max="255" value="255" class="slider" id="blue">
<p>Value: <span id="bluedemo"></span></p>
</div>
Functions in JavaScript are first-class citizens, meaning you can treat a function as a variable:
var changeBackground = function () {…};
red.oninput = changeBackground;
blue.oninput = changeBackground;
…
The other solution - Makhiel's is also correct is:
red.oninput = blue.oninput = green.oninput = function() {
&& is 'logical and' in JavaScript: it always turns into true or false. Though "Set red.oninput and blue.oninput to the same function" works in english, the and in English doesn't translate to the and in Javascript.
It's pretty straight forward using the HTMLFormControlsCollection API.
Changed the elements into more semantic as well as functional form control elements: <form>, <output>, <fieldset>,<legend>, and <label>. Used Template Literals to get all 3 values to mix in one value.
The key to reducing the number of functions and events is to use Event Delegation by having an element that is a common ancestor to the ranges, listen for the input event. Then by referencing the Event.target we can determine exactly which range is interacting with the user.
BTW in this case, there's no need to convert any values to a number because the value the CSS takes as a RGB color is actually a string that has chars that represent numbers.
Update
Added a second Demo that does the same thing as Demo 1, but with very minimal code by using On Event Attributes. Although the use of them is strongly discouraged I've seen a growing trend of it's use in current but minor code such as this.
Demo 1
Details commented in Demo
form {
display: flex;
flex-flow: column wrap;
height: 100vh;
}
.slidecontainer {
width: 40%;
height: 15vh
}
output {
transform: translate(5px, -7px);
display: inline-block;
}
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<form id='rgb'>
<fieldset class="slidecontainer">
<legend>Red</legend>
<input type="range" min="0" max="255" value="255" class="slider" id="red0">
<output id="redO"></output>
</fieldset>
<fieldset class="slidecontainer">
<legend>Green</legend>
<input type="range" min="0" max="255" value="255" class="slider" id="grn0">
<output id="grnO"></output>
</fieldset>
<fieldset class="slidecontainer">
<legend>Blue</legend>
<input type="range" min="0" max="255" value="255" class="slider" id="blu0">
<output id="bluO"></output>
</fieldset>
</form>
<script>
//Reference the form
var form = document.forms.rgb;
//Reference all of form's form controls
var rgb = form.elements;
//Register input event on form
form.addEventListener('input', composeColor);
// Callback function pass Event Object
function composeColor(e) {
//if the node interacting with user is type=range...
if (e.target.type === "range") {
// Reference active range
var rng = e.target;
// Get range's "little brother" output
var viewVal = rng.nextElementSibling;
// Sync their values
viewVal.value = rng.value;
// Collect the values of all ranges then interpolate them
// into a template literal.
// The TL is the color of body
document.body.style.background = `rgb(${red0.value}, ${grn0.value}, ${blu0.value})`;
} else return false;
}
</script>
</body>
</html>
Demo 2
On Event Attribute
form {
display: flex;
flex-flow: column wrap;
height: 100vh;
}
.slidecontainer {
width: 40%;
height: 15vh
}
output {
transform: translate(5px, -7px);
display: inline-block;
}
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<form id='rgb' oninput="document.body.style.background = `rgb(${red0.value},${grn0.value}, ${blu0.value})`">
<fieldset class="slidecontainer">
<legend>Red</legend>
<input type="range" min="0" max="255" value="255" class="slider" id="red0" oninput='redO.value = this.value'>
<output id="redO" for='red0'></output>
</fieldset>
<fieldset class="slidecontainer">
<legend>Green</legend>
<input type="range" min="0" max="255" value="255" class="slider" id="grn0" oninput='grnO.value = this.value'>
<output id="grnO" for='grn0'></output>
</fieldset>
<fieldset class="slidecontainer">
<legend>Blue</legend>
<input type="range" min="0" max="255" value="255" class="slider" id="blu0" oninput='bluO.value = this.value'>
<output id="bluO" for='blu0'></output>
</fieldset>
</form>
<script>
</script>
</body>
</html>
I have three input numbers:
<input min="0" name="cat1" step="1" type="number" max="50" value="0" />
<input min="0" name="cat2" step="1" type="number" max="50" value="0" />
<input min="0" name="cat3" step="1" type="number" max="50" value="0" />
Currently the max value is 50. I would like the max for all three together to be 50 aswell.
How can I solve this with javascript or jquery?
This method sums the other inputs, and if the sum + the current value is greater than max, it changes the current input's value to fit max.
For example, try entering 30, 5, 20 to the input boxes.
var max = 50;
var $inputs = $('input');
function sumInputs($inputs) {
var sum = 0;
$inputs.each(function() {
sum += parseInt($(this).val(), 0);
});
return sum;
}
$inputs.on('input', function(e) {
var $this = $(this);
var sum = sumInputs($inputs.not(function(i, el) {
return el === e.target;
}));
var value = parseInt($this.val(), 10) || 0;
if(sum + value > max) $this.val(max - sum);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input min="0" name="cat1" step="1" type="number" max="50" value="0" />
<input min="0" name="cat2" step="1" type="number" max="50" value="0" />
<input min="0" name="cat3" step="1" type="number" max="50" value="0" />
Hope you expected this way.
Try Using this code.
it will check the aggregate of three fields and check it whether it is greater than 50.
$(":input").bind('keyup mouseup', function () {
var cat1=$("#cat1").val();
var cat2=$("#cat2").val();
var cat3=$("#cat3").val();
var total=parseInt(cat1) + parseInt(cat2) + parseInt(cat3);
if(total>50) {
alert("it exceeds 50");
return false;
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input min="0" id="cat1" name="cat1" step="1" type="number" max="50" value="0" />
<input min="0" id="cat2" name="cat2" step="1" type="number" max="50" value="0" />
<input min="0" id="cat3" name="cat3" step="1" type="number" max="50" value="0" />
This will ensure the sum doesn't exceed 50. In case the new value makes the total exceed the limit, the other two will be lowered equally to enforce the total=50 rule.
Note the
let share2 = Math.min(Math.floor(excessValue / 2), elmt2Value) which ensures
To ensure we won't remove more from one of the input than its value.
<input min="0" name="cat1" id="cat1" step="1" type="number" max="50" value="0" onchange="valueChanged('cat1')" />
<input min="0" name="cat2" id="cat2" step="1" type="number" max="50" value="0" onchange="valueChanged('cat2')" />
<input min="0" name="cat3" id="cat3" step="1" type="number" max="50" value="0" onchange="valueChanged('cat3')" />
<script>
function valueChanged(changedElmtId) {
// Retrieve all input, the one changed and the other 2
let elmt2Id = "cat2", elmt3Id = "cat3"
if ( changedElmtId === elmt2Id ) {
elmt2Id = "cat1"
}
else if ( changedElmtId === elmt3Id ) {
elmt3Id = "cat1"
}
let changedElmt = document.querySelector("#" + changedElmtId)
let elmt2 = document.querySelector("#" + elmt2Id)
let elmt3 = document.querySelector("#" + elmt3Id)
let elmt2Value = parseInt(elmt2.value)
let elmt3Value = parseInt(elmt3.value)
// Check if any action is needed
let totalValue = parseInt(changedElmt.value) + elmt2Value + elmt3Value
if ( isNaN(totalValue) || totalValue <= 50 )
return
// Measure excess then split in 2
let excessValue = totalValue - 50
let share2 = Math.min(Math.floor(excessValue / 2), elmt2Value)
let share3 = excessValue - share2
console.log("Current:", " " + elmt2Id + ": ", share2, " " + elmt3Id + ": ", share3)
console.log("Total:", totalValue, " Excess: ", excessValue, " " + elmt2Id + ": ", share2, " " + elmt3Id + ": ", share3)
elmt2.value = elmt2Value - share2
elmt3.value = elmt3Value - share3
}
</script>
I don't know how optimal my solution is but it works. Covered some edge cases too.
let inputs = document.querySelectorAll('input');
const max = inputs[0].getAttribute("max");
let value;
inputs.forEach((input, i) => {
input.addEventListener('change',(e) =>handleChange(e)); // adding change event
input.addEventListener('blur',(e) =>handleChange(e)); //adding a blur eventto prevent direct typing of input larger than max
})
const handleChange = (e) =>{
if(e.target.value > max - value){
e.target.value = max-value; //setting max value possible if input value is greater than max
return;
}else{
value = 0;
inputs.forEach( input => {
value+=parseInt(input.value); //calculating value to negate from max
});
inputs.forEach( input => {
if(max-value > 0){
input.setAttribute("max", max-value); //setting max value on every input
}
});
}
}
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
</head>
<body>
<input min="0" name="cat1" step="1" type="number" max="50" value="0" />
<input min="0" name="cat2" step="1" type="number" max="50" value="0" />
<input min="0" name="cat3" step="1" type="number" max="50" value="0" />
</body>
</html>
I hope that works for you easy small and fast for lesser number of input in form.
$(function(){
$('input[type=number]').attr('max', '50')
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input min="0" name="cat1" step="1" type="number" value="0" />
<input min="0" name="cat2" step="1" type="number" value="0" />
<input min="0" name="cat3" step="1" type="number" value="0" />
I'm trying to display values of every slider I have on my page, this is my code so far:
var i = 0;
var st = 'slider';
var ot = 'output';
var s = '';
var o = '';
for (var x = 0; x < 3; x++) {
i++;
s = st+i;
o = ot+i;
var s = document.getElementById("range"+i);
var o = document.getElementById("limit"+i);
o.innerHTML = s.value;
s.oninput = function() {
o.innerHTML = this.value;
}
}
<div id="slidecontainer">
<input type="range" min="2" max="50" value="20" class="slider" id="range1" >
<label>You chose <span id="limit1"></span></label>
</div>
<div id="slidecontainer">
<input type="range" min="2" max="50" value="20" class="slider" id="range2" >
<label>You chose <span id="limit2"></span></label>
</div>
<div id="slidecontainer">
<input type="range" min="2" max="50" value="20" class="slider" id="range3" >
<label>You chose <span id="limit3"></span></label>
</div>
It's only changing the last value when I move any slider, I want to display the value of each slider respectively. I'm using a loop in my JavaScript code because I have more than 20 sliders and I don't want to write a function for each of them unless that is the only way of doing it. Any suggestions?
The problem you are having is related to variable scope. There is only one variable named o, each iteration of the loop changes this variable. So when the
oninput function is evaluated o equals the last value you set it to equal. The current value of o is not "saved" in the function definition.
See https://www.w3schools.com/js/js_scope.asp for more information.
See solution below, here I find the limit in each call to the function.
function updateLabel() {
var limit = this.parentElement.getElementsByClassName("limit")[0];
limit.innerHTML = this.value;
}
var slideContainers = document.getElementsByClassName("slidecontainer");
for (var i = 0; i < slideContainers.length; i++) {
var slider = slideContainers[i].getElementsByClassName("slider")[0];
updateLabel.call(slider);
slider.oninput = updateLabel;
}
<div class="slidecontainer">
<input type="range" min="2" max="50" value="20" class="slider">
<label>You chose <span class="limit"></span></label>
</div>
<div class="slidecontainer">
<input type="range" min="2" max="50" value="20" class="slider">
<label>You chose <span class="limit"></span></label>
</div>
<div class="slidecontainer">
<input type="range" min="2" max="50" value="20" class="slider">
<label>You chose <span class="limit"></span></label>
</div>
The filters work in unison in Firefox (43.0.1), but not in Chrome (57.0.2987.133). In Chrome, when I move my mouse over another slider, the previous slider's adjustments are removed. There is a similar question regarding image filters, but it does apply to the Firefox vs. Chrome issue.
var degrees=0;
var percentB=100;
var percentC=100;
function setValues() {
form1.brightness.value=100;
form1.contrast.value=100;
form1.hue.value=0;
}
function bright() {
percentB=form1.brightness.value;
document.getElementById("I").style.filter="brightness("+parseInt(percentB)+"%)"+" contrast("+parseInt(percentC)+"%)"+" hue-rotate("+parseInt(degrees)+"deg)";
}
function cont() {
percentC=form1.contrast.value;
document.getElementById("I").style.filter="brightness("+parseInt(percentB)+"%)"+" contrast("+parseInt(percentC)+"%)"+" hue-rotate("+parseInt(degrees)+"deg)";
}
function hues() {
degrees=form1.hue.value;
document.getElementById("I").style.filter="brightness("+parseInt(percentB)+"%)"+" contrast("+parseInt(percentC)+"%)"+" hue-rotate("+parseInt(degrees)+"deg)";
}
<img src="xbutterfly.jpg" alt="Colorful Flowers" width="800" height="533" id="I" /><br><br>
<form name="form1" id="form1id" style="font-size:90%; text-align:center;">
Brightness: <input type="range" name="brightness" min="0" max="200" step="5" onmousemove="bright()" style="vertical-align:-7px;" /> Contrast: <input type="range" name="contrast" min="0" max="200" step="5" onmousemove="cont()" style="vertical-align:-7px;"
/> Hue: <input type="range" name="hue" min="0" max="360" step="5" onmousemove="hues()" style="vertical-align:-7px;" />
</form>
After fighting with this for 2 hours, I finally saw a solution after posting the question: combining everything into one function.
New HTML:
<img src="xbutterfly.jpg" alt="Colorful Flowers" width="800" height="533" id="I" /><br><br>
<form name="form1" id="form1id" style="font-size:90%; text-align:center;">
Brightness: <input type="range" name="brightness" min="0" max="200" step="5" onmousemove="apply()" style="vertical-align:-7px;" />
Contrast: <input type="range" name="contrast" min="0" max="200" step="5" onmousemove="apply()" style="vertical-align:-7px;"/>
Hue: <input type="range" name="hue" min="0" max="360" step="5" onmousemove="apply()" style="vertical-align:-7px;"/>
</form>
New JavaScript:
<script type="text/javascript">
<!--
var degrees = 0;
var percentB = 100;
var percentC = 100;
function setValues()
{
form1.brightness.value = 100;
form1.contrast.value = 100;
form1.hue.value = 0;
}
function apply()
{
degrees = form1.hue.value;
percentC = form1.contrast.value;
percentB = form1.brightness.value;
document.getElementById("I").style.filter = "brightness(" + parseInt(percentB) + "%)" + " contrast(" + parseInt(percentC) + "%)" + " hue-rotate(" + parseInt(degrees) + "deg)";
document.getElementById("I").style.WebkitFilter = "brightness(" + parseInt(percentB) + "%)" + " contrast(" + parseInt(percentC) + "%)" + " hue-rotate(" + parseInt(degrees) + "deg)"; // Not tested as I don't have these browsers. Used for older versions of Safari, Chrome, and Opera per https://www.w3schools.com/jsref/prop_style_filter.asp
}
-->
</script>