I found this code (on HTML5), and there is nothing wrong with it, it work just fine, but i dont get the whole process it make(the code create a virtual keyboard that writes on a textfield the letters you click)
the part i dont get it:
var i,c;
function init(){
i=document.getElementById('keyboard').getElementsByTagName('input');
for(c=0;c<i.length;c++) {
if(i[c].type==='button') {
i[c].addEventListener('onclick',makeClickHandler(c));
}
}
My mains doubt are the i[c]
Thanks in advance
var i,c; // global var declaration usually not clever
function init(){ // he creates a js function
var keyboardelement=document.getElementById('keyboard');
var inputfield=keyboardelement.getElementsByTagName('input');
for( var c=0;c<inputfield.length;c++) {
if(inputfield[c].type==='button') { // he runs over the dom tree
// under keyboard to find his buttons...
// now he adds on each of the buttons a OWN click handler which
//calls the
// routine "makeClickHandler" -wrong wording should be known as
// ClickHandler or maybee not s below
inputfield[c].addEventListener('onclick',makeClickHandler(c));
}
}
that is what he made and what i call not that clever cause now he has to implement or make for each button own routine to handle this. Maybee that is why he chose the wording makeclickhanler cause h creates them dynamically.
This here do it all including the evnet handler a "nice" display and a bit action. It will display the text you click together.
<html><body>
<form name="keyboard">
//could also be dynamically created but...
<div id="out"><div>
<input type="button" value="a" class="kbdbutton">
<input type="button" value="b" class="kbdbutton">
<input type="button" value="c" class="kbdbutton">
<input type="button" value="d" class="kbdbutton">
</form>
usually i would add also a ID attribute but
<script>
// get all buttons in one call
var kbd_btn=documentGetElementsByClassName("kbdbutton")
for (var i=0; I< kbd_btn.length;i++){
kbd_btn[i].id = kbd_btn[i].value;
}
// NOW every key has its own *corresponding* id free of charge
// which also makes the button interesting for a global event handler
// act like a man and use ONE handler
document.addEventListener("click", function(event) {}
var id=event.target.id;
if (id){ // not all click events have a id
console.log(id);
display=document.getElementById("out")
switch(id) {
case "a":
alert("a clicked")
case default:
display.innerHTML=display.innerHTML+id;
}
}
)
</script>
</body></html>
if i would a just few lines more and use the evil eval function i would have a nice complete pocket calculator...
Related
I have this code, it's about disabling and enabling a button of a form according to its check box input. It's getting executed on different pages, you can see the last two lines where I loop through the forms on the page, all works fine when there is one from in the page, however, on pages where there is more than one form, it doesn't matter which check box I check (second or third for instance) it always affects the first button.
you can notice there are two "console.log" statements, the first one returns the correct input element (so when there are two forms in the pages, you can see in the console the elements reference the correct inputs on the DOM), however, the second one always shows an element in the console referring to the first input in the DOM.
my guess is that there is something wrong with the way I add the event listener, any suggestions for making that work?
class Steps {
constructor(el) {
this.el = el;
this.bindDOM();
this.bindStepEvents();
}
bindDOM() {
this.checkboxSelectorStep = this.el.querySelector('.radio-selector-steps-enable');
this.submit = this.el.querySelector('button[type=submit]');
}
bindStepEvents() {
console.log(this.checkboxSelectorStep); // returns the correct element
this.checkboxSelectorStep.addEventListener('click', (ev) => {
console.log(this.checkboxSelectorStep); // always returns the first element
if (ev.target.checked) {
this.submit.removeAttribute('disabled');
} else {
this.submit.setAttribute('disabled', '');
}
});
}
}
const forms = Array.from(document.querySelectorAll('.radio-selector-steps-form'));
forms.map(form => new Steps(form));
<form method="get" class="radio-selector-steps-form">
<input type="checkbox"
name="checkbox-selector-step"
id="checkbox-selector-step"
class="radio-selector-steps-enable">
<label for="checkbox-selector-step"><span>some text</span></label>
<button type="submit" disabled="">button text</button>
</form>
<form method="get" class="radio-selector-steps-form">
<input type="checkbox"
name="checkbox-selector-step"
id="checkbox-selector-step"
class="radio-selector-steps-enable">
<label for="checkbox-selector-step"><span>some text</span></label>
<button type="submit" disabled="">button text</button>
</form>
The problem was that both of the inputs had the same id, and the labels are referencing that id, so hitting the input itself works fine but whenever the label is being clicked the first listener gets called.
(due to styling, clicking the input was not possible for my case so I would always end up with the wrong behavior until I tried to create a SO snippet for it and caught the issue)
Hoping this might help someone.
You need to use querySelectorAll instead of querySelector
bindDOM() {
this.checkboxSelectorStep = this.el.querySelectorAll(STEPS_ENABLE_CHKBOX);
this.submit = this.el.querySelector(STEPS_FORM_SUBMIT);
}
bindStepEvents() {
console.log(this.checkboxSelectorStep);
this.checkboxSelectorStep.forEach(function(elem) {
elem.addEventListener('click', (ev) => {
console.log(this.checkboxSelectorStep);
if (ev.target.checked) {
this.submit.removeAttribute('disabled');
this.update(JSON.parse(this.steps[0].primaryInput.value))
} else {
this.submit.setAttribute('disabled', '');
}
});
});
Pretty sure I know the solution... would write .on('change','load', function(){}
correct? <-- Tested didn't work? so I am up to your solutions :)
Sushanth -- && adeneo both came up with great solutions, this is a good lesson in optimizing code... It's gonna be hard to choose which answer to go with, but I know this is going to help me rethink how I write... I dont know what I do without this forum, id have to learn this stuff in college.
This is purely a question out of curiosity and bettering my skills, as well as giving you guys a chance to display your knowledge on jQuery. Also to prevent any sloppy writing.
I have a radio based switch box, the markup looks like this, the id's and on/off values are generated by the values in my array with PHP...
<span class="toggle-bg">//This color is the background of the toggle, I need jQuery to change this color based on the state on/off
<input type="radio" value="on" id="_moon_page_header_area1" name="_moon_page_header_area">//this is my on value generated by the array
<input type="hidden" value="_moon_page_header_area" class="switch-id-value">// I create this input because I have multiple checkboxes that have the ID _moon_ARRAYVALUE_area1
<input type="radio" value="off" id="_moon_page_header_area2" name="_moon_page_header_area">// off value
<input type="hidden" value="_moon_page_header_area" class="switch-id-value">//_moon_ARRAYVALUE_area2
<span class="switch"></span>// the switch button that changes
</span>
Hope that makes sense and the comments are clear
Here is the jQuery
var value = $('.toggle-bg input.switch-id-value').val()
var moon1 = $('#'+value+'1').is(':checked');
var moon2 = $('#'+value+'2').is(':checked');
var static_slide = $('._moon_staticarea_height');
var toggle = $('.toggle-bg');
if(moon1){
toggle.css({'background-color': '#46b692'});
static_slide.hide()
} else
if (moon2){
toggle.css({'background-color': '#333'});
static_slide.show()
}
$('.toggle-bg').change(function () {
var value = $('.toggle-bg input.switch-id-value').val()
var moon1 = $('#'+value+'1').is(':checked');
var moon2 = $('#'+value+'2').is(':checked');
var static_slide = $('._moon_staticarea_height');
var toggle = $('.toggle-bg');
if(moon1){
toggle.css({'background-color': '#46b692'});
static_slide.slideUp()
} else
if (moon2){
toggle.css({'background-color': '#333'});
static_slide.slideDown()
}
});
it looks longer than it really is, its just repeating it self, one is on load so that it gives the correct color on load of the page, and then inside the change function we need to change colors..
How do I write it so I only have to use variables one time (so its cleaner) is there a better way to optimize it... Just NOW thinking after writing this I could put it in one function .on('load', 'change', function() {}
I just now thought of that, but I wrote all this so I am going to see what others think...
You'd do that by having the function in the change event handler, and on the end you chain on a trigger('change') to make it work on pageload :
$('.toggle-bg').on('change', function () {
var value = $('.toggle-bg input.switch-id-value').val(),
moon1 = $('#' + value + '1').is(':checked'),
slider = $('._moon_staticarea_height'),
toggle = $('.toggle-bg');
toggle.css('background-color', (moon1 ? '#46b692' : '#333'));
slider[moon1?'slideUp':'slideDown']();
}).trigger('change');
As radiobuttons can't be unchecked, it's either moon1 or moon2, which means checking one of them should be enough.
.on('change','load',
supposed to be
// Remove the comma separator if you want to bind the same handler to
// multiple events.
.on('change load',
And you can remove the one separately written out and enclose it in a function (if multiple instances of the class toggle-bg)
or just trigger the change event.(If there is a single instance of a class)
This will just run the same functionality when the page loads.
var toggle = $('.toggle-bg');
toggle.change(function () {
var value = $('input.switch-id-value', this).val(),
moon1 = $('#' + value + '1').is(':checked'),
moon2 = $('#' + value + '2').is(':checked'),
static_slide = $('._moon_staticarea_height');
if (moon1) {
toggle.css({
'background-color': '#46b692'
});
static_slide.slideUp()
} else if (moon2) {
toggle.css({
'background-color': '#333'
});
static_slide.slideDown()
}
}).change();
I am working on the project to create one variant of calculator. Basically, I am storing all the user clicks (which may be number and operator like +,-) in an array and handing the array to the another function when user clicks "=". I am using javascript for this. The below is my code:
var arr=[]; //array of every input from the user interface stored
var i=0; //number of input clicks from the user
var opPos=[]; //position of the operator as given by the user
var operAnd=[];//operands as given by the user
var disp='';
function clearval() {
// This function clears all the values stored in related array when C or CE is pressed
document.getElementById("op").value=0;
arr=[];
i=0;
opPos=[];
operAnd=[];
disp='';
}
//This Function get the value and supplies the array to calculating function
function getval(inp){
if(inp!="=")
{
arr[i]=inp;
disp=disp+inp;//for display in the screen of the output screen
document.getElementById("op").value=disp;
if (typeof inp!="number"){
opPos.push(i);
operAnd.push(inp);
}
i++;
}
else
{
var newInput=assembler(arr,opPos,operAnd);
clearval();
getval(parseFloat(newInput,10));
}
}
//<!------This Function calculates based on array, operator position and operands------!>
function assembler(num_array,opPos,operAnd){
var num='';
var numCollect=[];
var posCount=0;
for(var j=0;j<num_array.length;j++){
if (j==opPos[posCount]) {
numCollect.push(parseFloat(num,10));
num='';
j++;
posCount++;
}
else if (j>posCount) {
}
num=num+num_array[j];
}
num=parseFloat(num,10);
numCollect.push(num);
//document.getElementById("op").value= numCollect;
var newInput=calculator(numCollect,operAnd);
return newInput;
}
function calculator(target_num,operAnd) {
// Not the nice solution but straightforward nonetheless
var result='';
for (var l=0;l<operAnd.length;l++) {
result=result+target_num[l]+operAnd[l];
}
result=result+target_num[l];
document.getElementById("op").value=result + '=' + eval(result) ;
return eval(result);
}
I have html which has buttons like this:
<button class="num" onclick="getval(0)">0</button>
<button class="num" onclick="getval(1)">1</button>
<button class="num" onclick="getval(2)" >2</button>
<button class="num" onclick="getval(3)" >3</button>
......................... and so on
For the basic math, this code works fine and is not a problem. However, here is my problem from where I have hard time on thinking how to implement this. Say for example, I have a following button like this.
<button class='extra' onclick="Regression()"> Find Regression </button>
Now, I will have regression function which will ask user to input the regression type (1-linear, 2-quadratic and so on...this is just an example).
function regression(){
clearval();
document.getElementById("op").value=" Enter the degree of regression:";
which is basically asking user to enter the number and click '=' to enter into the program.
Now you can see my dilemma. Anything user inputs will be firstly processed by getval() which will pass the array to another function when user clicks '=', which is not what I will want in this case. To be clear, for this kind of case which I will have many such as std. dev or some kind of functions like this, I want the keypad to behave as normal keypad and pass the value to another function without doing normal calculator stuff as it was supposed to do.
Am I thinking this straight? Any suggestions?
I think you could solve this by adding another function called passval(), which will contain much of the same logic as getval() in terms of parsing the input into a float, etc., but which doesn't ever push values onto your operand stack or call the assembler function. It simply returns the button pushed as a nice float value to the .extra function that called it.
Then, as part of the logic in your .extra functions like Regression(), you would initially swap the onclick function for all of your buttons from getval() to passval(). When the regression or other special function is complete, swap the buttons back to their default behavior.
Well, this is what I would do(if I undestood right):
Change you button layout to this:
<input type="button" class="num" value="0" /> Removing the onclick event
Always better to use input type="button" than <button>.
Create a function to bind events to the buttons:
function bindButtonEvent(func) {
var buttons; // Here some way to get the button in a collection from the DOM tree
for (var i = 0; i < buttons.length; i++) {
buttons[i].onclick = function() { func(buttons[i]); }
}
}
With this you will got a function to change the click event of all your buttons. This will make your engine more flexible.
You will have to change your getval function a little to get the value by itself:
function getval(el){
var inp = el.value;
if(inp!="=")
So on the calculator load, you set the click function:
bindButtonEvent(getval())
When you want to call a custom behaviour function, you call the binding again:
function regression() {
bindButtonEvent(function(el) {
value = el.value;
// Do things
// When done, take bindings back.
bindButtonEvent(getval());
});
clearval();
document.getElementById("op").value=" Enter the degree of regression:";
}
NOTE: That is an ideia. I didn't tested the code. If you're interested on this and have errors on implementation, let me know, and we'll going fixing them.
I am having problems with Javascript event handler functions created dynamically.
In my HTML I have the following:
<input id="nvctrlfont1" type=number min=18 max=144 step=1 value=36 maxlength="3" size="8">
<input id="nvctrlfont2" class="color" value="000000" style="background-color:black;width:70px;">
<input id="nvctrlfont4" class="color" value="000000" style="background-color:black;width:70px;">
.....
The class color is just a javascript color picker.
The element ID's have 1,2,4 ... etc as Boolean values. That is to say a JS function receives a Boolean that is used to hide or display the HTML input elements. For elements that are hidden no events are to be assigned, for those that are not hidden, events are to be assigned.
The Events functions would reside in the nvFontFuncs.
The problem I cannot seem to solve:
When the function assignment takes place to the nvEL.onchange it appears to replace it each time with the same function.
For example, given the Boolean nvFontBool comes in with say "6" then what should happen is the Input Elements nvcontrolfont2 and nvcontrolfont4 should be enabled and have the functions in nvFontFuncs nvctrlfont2 and 4 respective be assigned to the onchange events accordingly.
What happens however is nvctrlfont4 function ends up getting assigned to BOTH elements.
In other words, no matter which element that has had its onchange event set, when clicked it calls the LAST one assigned. In my example, nvctrlfont4() gets called no matter if I change INPUT nvctrlfont2 or nvctrlfont4 elements.
If I for example change the nvFontBool parameter to "7" thus enabling all three input elements. Then no matter which one is changed, it calls nvctrlfont4() (which happens to be the last one assigned in the loop).
I tried using addEvent and setAttributes as well, same results. Its as the onchange assignment of the function is static when bound. So which ever is the most "recent" one bound ends up replacing the prior bindings across all elements respective.
Help?
Heres the JS.
var nvFontFuncs = {
nvctrlfont1: function () { alert('Function 1'); },
nvctrlfont2: function () { alert('Function 2'); },
nvctrlfont4: function () { alert('Function 4'); }
};
Here is the Function
function nvSetFontCtrls(nvFontBool)
{
var nvEL;
var nvName="nvctrlfont";
var nvTemp;
nvFg=1;
while(nvFontBool != 0) {
//alert("FontBool:"+nvFontBool);
nvTemp=nvName+nvFg.toString();
// nvTemp="func"+nvFg.toString();
nvEL=document.getElementById(nvTemp);
if(nvFontBool & nvFg) {
nvEL.style.display="block";
nvEL.onchange=function() { nvFontFuncs[nvTemp](); }
}
else {
nvEL.style.display="none";
}
nvFontBool=nvFontBool&(~nvFg); //alert("nvfontbool:"+dechex(~nvFg));
nvFg = nvFg << 1;
}
}
You assign the following function:
function() { nvFontFuncs[nvTemp](); }
nvTemp is a variable in the local scope of nvSetFontCtrls. So when the handler is called, then, of course, nvTemp has the last value that it was set to.
You could do somthing like this:
nvEL.onchange=(function(nvTemp) {
return function() { nvFontFuncs[nvTemp](); }
}(nvTemp));
The problem is, that nvTemp is set to the last value of the loop, so once the handler function was called, nvTemp will have 'nvctrlfont4'.
If you use nvEL.onchange=nvFontFuncs[nvTemp] assignment, the problem will be solved
Been having a bit of a problem for the last couple of days. I'm trying to streamline my code as much as possible and I have now got to the stage where I am trying to add Event Listeners via JavaScript so my HTML looks tidier.
-HTML Segment-
<input type="button" id="googleSearchButton" />
<input type="button" id="youtubeSearchButton" />
<input type="button" id="wikiSearchButton" />
<input type="button" id="facebookSearchButton" />
<input type="button" id="twitterSearchButton" />
<input type="button" id="tumblrSearchButton" />
<input type="button" id="dropboxSearchButton" />
JavaScript Segment
var contIDArray = ["google", "youtube", "wiki", "facebook", "twitter", "tumblr", "dropbox"];
window.load = initAll();
function initAll(){
applyProperties();
}
function applyProperties(){
for (var i = 0; i < contIDArray.length; i++){
addEventListeners(contIDArray[i] + "SearchButton");
}
}
function addEventListeners(id){
document.getElementById(id).addEventListener("click", testAlert(id), false);
}
function testAlert(id){
alert(id + " clicked")
}
The Theory
As, I hope, you can see, the FOR loop will loop until it runs out of values in the container Array. Each time it will output the place in the Array followed by "SearchButton". For example, the first time it loops it will output "googleSearchButton", the second time "youtubeSearchButton" and so forth.
Now, I know that the FOR loop works for applying properties because I use it to apply Button values and text box placeholder text in other segments of my project.
I have made it add a simple test function ("testAlert()") and set it to pass the id of the element that called it. I have set it up so once the event listeners have been added I can simply click on each button and it will alert its id and tell me that it has been clicked.
The Problem
Now, theoretically, I thought this would work. But it seems that the FOR loops fires the "addEventListeners" function, which, in turn, adds the event listener to fire "testAlert" on click. But it just fires the "testAlert" function as soon as it adds the event listener and does not fire when you click.
I apologise if this seems a bit much to take in, I always overdo the length of my explanation. Hopefully you'll be able to see what I'm trying to accomplish from my code, rather than my explanation.
Help would be much appreciated. :)
You're close here, but there are a few things wrong.
First, you can't just do id.addEventListener. You need to do document.getElementById(id).addEventListener. id is just a string, you need a DOMElement.
Second, when you do testAlert(id), you're running the function, then assigning its return value (undefined) as the event listener. You need to pass a function. Like so:
id.addEventListener("click", function(){
testAlert(this.id); // this is the DOMElement you clicked on
}, false);
Though I suggest adding a class to all your buttons, and then adding the event like that.
<input type="button" id="googleSearchButton" class="searchButton" />
<input type="button" id="youtubeSearchButton" class="searchButton" />
<input type="button" id="wikiSearchButton" class="searchButton" />
<input type="button" id="facebookSearchButton" class="searchButton" />
<input type="button" id="twitterSearchButton" class="searchButton" />
<input type="button" id="tumblrSearchButton" class="searchButton" />
<input type="button" id="dropboxSearchButton" class="searchButton" />
And then:
var buttons = document.getElementsByClassName('searchButton');
for(b in buttons){
if(buttons.hasOwnProperty(b)){
buttons[b].addEventListener("click", function(){
testAlert(this.id); // this is the DOMElement you clicked on
}, false);
}
}
NOTE: addEventListener and getElementsByClassName may not be available in all browsers (by that I mean they might not work in IE). This is why a lot of websites use a JavaScript library, like jQuery. jQuery handles all the cross-browser stuff for you. If you want to use jQuery, you could do this:
$('.searchButton').click(function(){
testAlert(this.id);
});
NOTE 2: In JavaScript, functions are variables, and can be passed as parameters.
document.getElementById(id).addEventListener('click', testAlert, false);
Notice how there are no () after testAlert, we are passing the function itself, when you do testAlert() you're passing its return value. If you do it this way, testAlert will need to be modified a bit:
function testAlert(){
alert(this.id + " clicked")
}
Change:
function addEventListeners(id){
id.addEventListener("click", testAlert(id), false);
}
for:
function addEventListeners(id){
document.getElementById(id).addEventListener("click", testAlert(id), false);
}
Otherwise you're applying addEventListener on a string.
In any case, replace addEventListener with an assignment to the event, like onClick.
id looks like a string to me. So instead do something like this:
function addEventListeners(id){
var obj = document.getElementById(id);
obj.addEventListener("click", testAlert(id), false);
}
Also, here is the working code:
http://jsfiddle.net/ZRZY9/2/
obj.addEventListener("click", function() { testAlert(id); }, true);
As Rocket mentions above "you're calling it and setting the event to the return value undefined".
The bad news is addEventListener() is currently not supported in Internet Explorer 7.
I ran through your code. The initial problem that I came across was that you were trying to find the elements in the document before they were created. window.onLoad fires before the page is complete. I tested this using the body tag's onload attribute and it works that way.
So, it's a combination of the aforementioned issue of your trying to find the element by using the "id" string and the function firing before the page was completely loaded.
Anyway, glad you got it working!
This is the javascript I had at the end:
<script>
var contIDArray = ["google", "youtube", "wiki", "facebook", "twitter", "tumblr", "dropbox"];
function initAll(){
applyProperties();
}
function applyProperties(){
for (var i = 0; i < contIDArray.length; i++){
var newString = contIDArray[i] + "SearchButton"
addEventListeners(newString);
}
}
function addEventListeners(id){
document.getElementById(id).addEventListener("click", testAlert, false);
}
function testAlert(){
alert(this.id + " clicked")
}
</script>