Javascript : Assign a function and pass parameter at the same time - javascript

I have html buttons "classA","classB" and "classC" to which I assign the onclick handler function as follows..
var classA = document.getElementById('classA');
classA.onclick = filterByClassA;
var classB = document.getElementById('classB');
classB.onclick = filterByClassB;
var classC = document.getElementById('classC');
classC.onclick = filterByClassC;
These 3 functions do the same thing, only difference being the class.
So, can I have a single function assigned to these buttons, called with different parameters for each button. Something like below
var classA = document.getElementById('classA');
classA.onclick = filterByClass('classA');
var classB = document.getElementById('classB');
classB.onclick = filterByClass('classB');
var classC = document.getElementById('classC');
classC.onclick = filterByClass('classC');
I know this is a function call and not assignment and this is wrong but is there a way I can achieve this i.e. assign a function and pass parameter at the same time but not call it?

function filterByClass(className)
{
return function()
{
// Do something with className
console.log(className);
}
}

Bind can help you out here: Its called partial application.
Bind Syntax
fun.bind(thisArg[, arg1[, arg2[, ...]]])
1st param is scope of the function when it is called.
From 2nd you can pass any number of agruments. See the below code to know how it works.
Code:
var classA = document.getElementById('classA');
classA.onclick = filterByClass.bind(classA, 'classA');
var classB = document.getElementById('classB');
classB.onclick = filterByClass.bind(classB, 'classB');
var classC = document.getElementById('classC');
classC.onclick = filterByClass.bind(classC, 'classC');
function filterByClass(className, eventObject) {
console.log(this, className, eventObject);
}
Update:
Check out the Compatibility section in the above MDN link. You may need to use it, if you are going to use bind in non modern browsers.

i always try to keep the code as short as possible
so if your buttons are inside a container you can do that.
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>atest</title>
<script>
window.onload=function(){
var c=document.getElementsByTagName('div')[0].childNodes,
l=c.length,
filterByClass=function(){
console.log(this.id);//this.id is the classA or whatever
};
while(l--){
c[l].onclick=filterByClass;
}
}
</script>
</head>
<body>
<div>
<button id="classA">A</button>
<button id="classB">B</button>
<button id="classC">C</button>
</div>
</body>
</html>
in this case
document.getElementsByTagName('div')[0]
returns the first div in the document
childNodes
give u the list of the buttons inside that div
the while function adds the onclick event with your function 'filterByClass'
inside filterByClass u can access the element by this and so return it's id with this.id

Related

How to call your own method using "this" on a DOM node

I would like to make something like this:
function alrtHtml() {
alert(this.innerHTML)
}
function myFunc(id) {
document.getElementById(id).alrtHtml()
}
<html>
<head>
<meta charset="UTF-8" />
<title>test</title>
</head>
<body>
<h1 id="h1">Hello world!</h1>
<input type="button" value="click" onclick="myFunc('h1')" >
</body>
</html>
The result should be an alert with the text "Hello world!" inside the h1 tag.
It is my goal to be able to do this without explicitly passing the element as an argument to alertHtml.
You generally don't want to extend native prototypes, and one way to create chainable methods without doing that, is to create your own method to get the elements, and then create another chainable method to alert the innerHTML, like most libraries do.
Probably the simplest example would be something like this
function getElement(selector) {
if (!(this instanceof getElement)) return new getElement(selector);
this.element = document.querySelector(selector);
return this;
}
getElement.prototype.alertHtml = function() {
alert(this.element.innerHTML);
return this;
}
function myFunc(id) {
getElement(id).alertHtml();
}
myFunc('#test');
<div id="test">TEST</div>
This way you're only extending your own objects, not native objects, and you can create any kind of chainable method to add to that.
What you're looking to do is add your function to the prototype of whatever type document.getElementById(id) returns.
In this case it's returning an Element, so in order to add your function to its prototype you would write the following code.
Element.prototype.alrtHtml = function() {
alert(this.innerHTML)
}
As another alternative, you could also pass the element right to alertHTML:
function alertHTML(el) {
alert(el.innerHTML)
}
function myFunc(id) {
var elArg = document.getElementById(id)
alertHTML(elArg)
// You could also write it like this:
/*
alertHTML(document.getElementById('h1'))
*/
}
<h1 id='h1'>Hello, world</h1>
<button onclick="myFunc('h1')">Button</button>
There's a plethora of reasons to do that, but the gist of it is to avoid having issues if somebody else creates a alertHTML method on the Element prototype.
EDIT: If you really want to use this, you might also like to learn about binding functions - funfunfunction made a good video on this here. Here's how that would work:
function alertHTML() {
alert(this.innerHTML)
}
function myFunc(id) {
var el = document.getElementById(id)
alertHTML.apply(el)
}
<h1 id='h1'>Hello, world</h1>
<button onclick="myFunc('h1')">Button</button>
apply runs whatever its function is with this as the first argument you pass to apply. (The rest of the arguments you pass to apply are passed directly to the function.)

HTML, Javascript: button doesn't call function

I make a button in HTML, and I want it to call function in javascript:
index.html
<body onLoad="setGameAreaBounds()" onResize="setGameAreaBounds()">
<div id="scoreLabel">Score: 0 </div>
<!--div Group-->
<div>
<p id="pageTitle">Button Chaser</p>
<input type="button" id="startButton" onClick="start()" value="Start"/>
</div>
<!--The following: gameArea and dot is grouped together-->
<div id="gameArea">
<button id="dot" onClick="detectHit()"></button>
</div>
</body>
buttonChaser.js
function detectHit() {
//Increase the var score
}
function setGameAreaBounds() {
//Setting the size of GameBoard/Window
}
function moveDot() {
//randomly move the button(id: dot) within the GameBoard/window
}
function start() {
alert("sometext");
moveDot();
}
The code runs fine if I put moveDot(); function inside setGameAreaBounds();
However, it seems like the button(id: startButton) never connects to function start();
What did I do wrong?
Try moving your functions inside your onLoad function and link them back up to the global scope:
var score = 0;
var aWidth;
var aHeight;
var timer;
var that = this;
function setGameAreaBounds() {
//Setting the size of GameBoard/Window
that.moveDot = function () {
//randomly move the button(id: dot) within the GameBoard/window
}
that.detectHit = function() {
//Increase the var score
}
that.start = _start; // link desired name to reference to existing function
}
function() _start {
alert("sometext");
moveDot();
}
Basically, your html functions are accessing functions defined in the global scope after the dom elements were created. So no reference to the function exists when the dom is created. The setGameAreaBounds function gets called after the dom is ready - onLoad. JavaScript functions each have their own scope so you need to pass this from the parent using an unique reference. Then you can assign the names you want to the functions.
A better approach would be to define all scripts after
Many programs use an onReady function that waits until the dom is loaded before defining any javascript functions. This is a good approach.

Is there a way to use this.onClick event and still be able to use "this" in function it calls?

project with these needs.
(10 points) When a button is clicked, the appropriate points are added to the total score.
(10 points) You are Not allowed to make any change to the HTML code provided above. In other words, you need to write unobtrusive JavaScript code, attaching event handlers in JavaScript rather than in HTML.
(10 points) The window.onload event handler should be an anonymous function.
(10 points) You should use the same event handler to handle the onclick event occurred on all of the four buttons on the webpage. In other words, you need to avoid code redundancy by using the this keyword.
I would like if someone could tell me a way I can use "this" in both of my functions so it doesn't have to be repetative like it is now in the first function, something like what I commented out. Or a way to simplify the code so it works as it does now.
<script>
window.onload = pageLoad();
function pageLoad() {
//this.onclick = okClick;
document.getElementById("6pt").onclick = okClick;
document.getElementById("3pt").onclick = okClick;
document.getElementById("2pt").onclick = okClick;
document.getElementById("1pt").onclick = okClick;
}
function okClick() {
var num1 = document.getElementById("score").value;
num1 = parseInt(num1);
var num2 = num1 + parseInt(this.id[0]);
document.getElementById("score").value = num2;
}
</script>
<body>
<div>
<input id="score" type="text" value="0" />
</div>
<div>
<button id="6pt">6 points (touchdown)</button>
<button id="3pt">3 points (field goal)</button>
<button id="2pt">2 points (safety/2-point conversion)</button>
<button id="1pt">1 point (extra point)</button>
</div>
</body>
Why don't you give all those elements a class, and then use
var myLinks = document.querySelectorAll('.myClass');
for(var i=0;i<myLinks.length;i++)
myLinks[i].onclick = okClick;
Inside okClick, the clicked element will be available via this (and also event.target).
Event handlers, like onclick are called in the context of the target object, thus you can safely use this within any event handler function. This also applies to jQuery attached events.
The above is valid for the 'okClick' function. You asked about "both of my functions", so if you're referring to pageLoad, that function is not executed in the context of an object, so you can't use this. But why would you need it? It won't help you much.
You can use the bind function which will give a given context to your function:
var context = window;
context.something = 5;
var myFunc = function () {
console.log(this.something); //5
}.bind(context);
If all you want to do is attach the same okClick() to each button then you should try and use a more general selector than ID. You can only have one ID per element so, like you've noticed, you must individually hook into each one.
Instead, try a more general selector, like document.getElementsByClassName("className");, which would be a bit more general:
<body>
<div>
<input id="score" type="text" value="0" />
</div>
<div>
<button id="6pt" class="score-button">6 points (touchdown)</button>
<button id="3pt" class="score-button">3 points (field goal)</button>
<button id="2pt" class="score-button">2 points (safety/2-point conversion)</button>
<button id="1pt" class="score-button">1 point (extra point)</button>
</div>
<script>
(function(){
// Same as yours
function okClick() { ... }
var buttons = document.getElementsByClassName("score-button");
for(var i = 0, len = buttons.length; i < len; i++)
{
buttons[i].onClick = okClick;
}
}).call(this);
</script>
</body>
There's fancier selectors available in either jQuery or more modern javascript implementations which will save you having to use a class selector, you'll have to read into that though - or see Sidd's answer, which is doing exactly that (it's cleaner).

how to fix the closure bug in my jQuery code

the page always alert "btn2" ,whenever I click btn1 or btn2. it's seem the problem caused the "click" closures.but I don't know how to fixed it.
thanks in advance.
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>untitled</title>
<script src="http://code.jquery.com/jquery-1.10.2.min.js"></script>
<script>
(function(){
$.fn.test = function(){
F.init.apply(this,arguments)
};
var F ={
that:null,
init:function(){
F.that = this;
$(F.that).click(function(e) {
F.method();
});
},
method:function(){
var text =$(F.that).html();
alert(text);
}
}
})();
$(function(){
$("#btn1").test(); //alert btn2---bug
$("#btn2").test(); //alert btn2
});
</script>
</head>
<body>
<button id="btn1">btn1</button>
<button id="btn2">btn2</button>
</body>
</html>
You have referred to your object class F by that name from within the class.
Your line F.that = this is effectively therefore creating what would in other OO languages be considered a "static member" of the class, so both #btn2 and #btn1 ended up sharing the same that member.
Furthermore, your click handler is trying to call F.method() - in effect also a static method call.
You'll need to create a new object of type F each time you wish to wrap it around an element. Only then will you get a separate this for each element.
I suggest using an off-the-shelf jQuery plugin model such as http://jqueryboilerplate.com/ instead of trying to invent your own. See this extract from that code:
$.fn[ pluginName ] = function ( options ) {
return this.each(function() {
if ( !$.data( this, "plugin_" + pluginName ) ) {
$.data( this, "plugin_" + pluginName, new Plugin( this, options ) );
}
});
};
Note how it uses new Plugin to create the plugin instance, and then stores it on the element using $.data, but only the first time the plugin is invoked against each element.
1)F is a static Object
2)So F.that is also static
3)so
F.that = this
line will set 'this' to F.that.
4)first time you call
$("#btn1").test();
then F.that will be equal to $("#btn1");//this will be equal to $("#btn1")
5)Next time you call
$("#btn2").test();
then F.that will be equal to $("#btn2");//this will be equal to $("#btn2")
6) So finally to F.that you are setting $("#btn2")
7)hence the $(F.that).html(); is essentially $($("#btn2").html()) which is further same as $("#btn2").html()
8)Hence alert is showing "btn2"

create multiple divs with the same class javascript

I am new to JavaScript and would like to know how I can create multiple divs dynamically with the same class name. I have the following code but it only creates one instance of the div.
<div id="wrapper">
<div id="container">
<div id="board">
<script>
var board = document.createElement('div');
board.className = "blah";
for(x=0; x<9;x++) {
document.getElementById('board').appendChild(board);
}
</script>
</div>
</div>
</div>
Right now, you're creating the element outside the loop, and appending that element to the DOM...again and again.
What you want to do is create a new element during every iteration of the loop. To do that, move the part where you create the new div inside the loop:
for(x=0; x<9;x++) {
var board = document.createElement('div');
board.className = "blah";
document.getElementById('board').appendChild(board);
}
Now, every time the loop runs, you'll create a new element, and append that element to the element with ID #board.
It's worth pointing out that the variable you created (board) now only has scope within this loop. That means that once the loop is done, you'll need to find a different way to access the new elements, if you need to modify them.
Only a single element is created.
<script>
var board = document.createElement('div');
board.className = "blah";
for(x=0; x<9;x++) {
document.getElementById('board').appendChild(board);
}
</script>
Should be written as:
<script>
for(x=0; x<9;x++) {
var board = document.createElement('div');
board.className = "blah";
document.getElementById('board').appendChild(board);
}
</script>
Others did answer the question in a nutshell; here is one approach which addresses some issues that are present in the your and proposed code snippets, and maybe gives your some insight for further exploration. I hope it helps :)
To extend a script a little bit, this solution creates every element by using function createDiv, and references to individual divs are stored in an array, so you can modify the content of each div by modifying array elements, which are referring to DOM elements. (in this example, I modify 6th div for demonstration sake)
Notes:
All of your code is thrown in a global object, it's good
practice to encapsulate your code, here in immediately invoked
anonymous function.
x would be thrown in a global object even if encapsulated, you need
always to declare your variables with a var keyword. Here I declare
all vars needed upfront in one statement, which is also a good
practice;
It is convention to use "i" for loop iterator variable.
Avoid "magic numbers" (9), rather create a variable that will
describe what you do in your code. It is good if the code describes what
it does.
Also in this example, we avoid declaring "board" on each loop
iteration (the element where your divs get appended.)
Test your code in JSLint - great tool to validate your scripts.
(this will pass the test, given that you set indentation to 2.
"use strict" - read here.
/*jslint browser:true */
(function () {
"use strict";
function createDiv() {
var boardDiv = document.createElement("div");
boardDiv.className = "new-div";
boardDiv.innerText = "I am new DIV";
return boardDiv;
}
function createAndModifyDivs() {
var board = document.getElementById("board"),
myDivs = [],
i = 0,
numOfDivs = 9;
for (i; i < numOfDivs; i += 1) {
myDivs.push(createDiv());
board.appendChild(myDivs[i]);
}
myDivs[5].className = "modified-div";
myDivs[5].innerText = "I'm modified DIV";
}
createAndModifyDivs();
}());
.new-div {
color: gray;
}
.modified-div {
color: red;
}
<!DOCTYPE html>
<html>
<head>
<title>Inserting Divs</title>
</head>
<body>
<div id="wrapper">
<div id="container">
<div id="board">
</div>
</div>
</div>
</body>
</html>

Categories