Make a div clickable in JavaScript - javascript

I am trying to dynamically make divs that are clickable. I have inserted a test function. The test function runs even though the div has not been clicked.
function displayResults(responseTxt)
{
var results = document.getElementById("results");
jsonObj = eval ("(" + responseTxt + ")");
var length = jsonObj.response.artists.length;
results.innerHTML = "Please click on an artist for more details: "
for ( var i = 0; i < length; i++)
{
var entry = document.createElement("div");
var field = document.createElement("fieldset");
entry.id = i;
entry.innerHTML = i + 1 + ". " + jsonObj.response.artists[i].name;
field.appendChild(entry);
results.appendChild(field);
//entry.addEventListener("click", idSearch(jsonObj.response.artists[i].id), false);
entry.addEventListener("click", test(), false);
}
} // end function displayResults
function test()
{
document.getElementById("results").innerHTML = "tested";
}

You are calling the test() function and passing its return value to .addEventListener(). Remove the parentheses:
entry.addEventListener("click", test, false);
So that you pass the function itself to .addEventListener().
That answers the question as asked, but to anticipate your next problem, for the line you've got commented out you'd do this:
entry.addEventListener("click",
function() {
idSearch(jsonObj.response.artists[i].id);
}, false);
That is, create an anonymous function to pass to .addEventListener() where the anonymous function knows how to call your idSearch() function with parameters. Except that won't work because when the event is actually triggered i will have the value from the end of the loop. You need to add an extra function/closure so that the individual values of i are accessible:
for ( var i = 0; i < length; i++)
{
var entry = document.createElement("div");
var field = document.createElement("fieldset");
entry.id = i;
entry.innerHTML = i + 1 + ". " + jsonObj.response.artists[i].name;
field.appendChild(entry);
results.appendChild(field);
// add immediately-invoked anonymous function here:
(function(i) {
entry.addEventListener("click",
function() {
idSearch(jsonObj.response.artists[i].id);
}, false);
})(i);
}
That way the i in jsonObj.response.artists[i].id is actually going to be the parameter i from the anonymous function which is the individual value of i from the loop at the time each iteration ran.

Related

Javascript button.onclick not functioning like I thought

So I was in the presumption that this function
button.onclick = exampleFunk;
would give me a handler on each button when I click them, but it doesn't. When replacing it with:
button.onclick = alert("bananas");
I'm getting alerts at page onload. The problem is already solved with this:
button.setAttribute("onclick", "removeIssue(this)");
Out of curiousity... What's going on?
edited layout of post
EDIT
var issues = [];
window.onload = function () {
//alert("venster geladen");
issuesToList()
}
function issuesToList(data) {
/*alert(
"array length is " + data.issues.length + "\n" +
"total_count is " + data.total_count + "\n" +
"limit is " + data.limit + "\n" +
"offset is " + data.offset + "\n" + ""
);*/
for (i = 0; i < data.issues.length; i++) {
issue = data.issues[i];
createIssue(issue);
}
}
function createIssue(issue){
var id = issue.id;
var tracker = issue.tracker;
var status = issue.status;
var priority = issue.priority;
var subject = issue.subject;
var description = issue.description;
var assignee = issue.assignee;
var watchers = issue.watchers;
var ticket = new Issue(id, tracker, status, priority, subject, description, assignee, watchers);
issues.push(ticket);
var button = document.createElement("button");
button.innerHTML = "-";
button.onclick = function (){ alert("bananas")};
//button.setAttribute("onclick", "removeIssue(this)");
var item = document.createElement("div");
item.setAttribute("id", id);
item.appendChild(button);
item.innerHTML += " " + subject;
var container = document.getElementById("container");
container.appendChild(item);
}
function removeIssue(e){
var key = e.parentNode.getAttribute("id");
var count = issues.length;
if(confirm("Confirm to delete")){
for(i=0; i<count; i++){
if (issues[i].id == key ){
issues.splice(i,1);
var element = document.getElementById(key);
element.parentNode.removeChild(element);
}
}
}
}
function Issue(id, tracker, status, priority, subject, description, assignee, watchers){
this.id = id;
this.tracker = tracker;
this.status = status;
this.priority = priority;
this.subject = subject;
this.description = description;
this.assignee = assignee;
this.watchers = watchers;
}
EDIT
<body>
<h1>List of Issues</h1>
<div id="container"></div>
<script src="http://www.redmine.org/issues.json?limit=10&callback=issuesToList"></script>
</body>
You need to mask the alert in a function:
button.onclick = function (){ alert("bananas")};
As such:
var btn = document.createElement("BUTTON");
var t = document.createTextNode("CLICK ME");
btn.appendChild(t);
btn.onclick = function() {alert("bananas")};
document.body.appendChild(btn);
Whats going on?
You alert() is executed on page load because its a function call. When the execution of your script reaches that line your assignment
button.onclick = alert("bananas");
is actually executing the alert statement and not assigning it to button.onclick
You can bind arguments to the function so that it returns with the function you want it to call using your arguments (with additional arguments passed later added on to the end). This way doesn't require writing extraneous code (when all you want to do is call a single function) and looks a lot sleeker. See the following example:
button.onclick = alert.bind(window, "bananas");
An unrelated example of how it works in your own code is like this:
var alert2 = alert.bind(window, 'Predefined arg');
alert2(); // 'Predefined arg'
alert2('Unused'); // 'Predefined arg'
For IE, this requires IE9 as a minimum. See MDN for more information.
EDIT: I've looked closer at your code and there was one significant change that was needed for it to work... You cannot add onto the innerHTML when you've added JavaScript properties to a child element. Changing the innerHTML of the parent element will convert your element into HTML, which won't have the onclick property you made before. Use element.appendChild(document.createTextNode('My text')) to add text dynamically.
See a functioning example here: http://jsfiddle.net/2ftmh0gh/2/

Saving specific value Javascript

So when I execute the following code it gets the value. Though the program goes through and clicks some stuff for me and the value naturally changes. But I wish to save the value of this beforehand and then compare it to the second value.
Executed code :
var Category = [];
var ID1;
var ID2;
var dispatchMouseEvent = function(target, var_args) {
var e = document.createEvent("MouseEvents");
e.initEvent.apply(e, Array.prototype.slice.call(arguments, 1));
target.dispatchEvent(e);
}
var Level1Cats = document.getElementsByClassName("p-pstctgry-lnk-ctgry "); //GETTING LEVEL 1 CATS
var Level1CatsLen = Level1Cats.length; //GETTING LEVEL 1 CAT LEN
for (i = 0; i <= Level1CatsLen-1; i++) {
var ID1 = Level1Cats[i].id;
var temp1 = Level1Cats[i].innerHTML;
temp1.replace(/&/gi, "&").replace(/<[^>]*>/gi, "");
function GoToLevel2(i) { //GO TO NEXT LEVEL!
dispatchMouseEvent(Level1Cats[i], "mouseover", true, true);
dispatchMouseEvent(Level1Cats[i], "click", true, true);
}
function GetLevel2() { //GET NEXT LEVEL
var Level2Cats = document.getElementsByClassName("p-pstctgry-lnk-ctgry");
return Level2Cats.length;
}
setTimeout(GoToLevel2(i),100); //RUN IT WITH TIMING
var Level2CatsLen = GetLevel2();
// END OF LEVEL 1
var extracats2 = Level2CatsLen - Level1CatsLen;
console.log(extracats2+"e");
if (extracats2 !== 2 || extracats2 !== 0) {
for (ii = 0; ii < extracats2; ii++) { //LEVEL 2
console.log(ii);
ID2 = Level2Cats[ii+Level1CatsLen].id;
var temp2 = Level2Cats[ii+Level1CatsLen].innerHTML;
temp2.replace(/&/, "&").replace(/<[^>]*>/gi, "");
var Level2Children = [];
for (l = 0; l < level1CatsLen; l++) {
Level2Children.push(Level2Cats[l].id);
}
//DO SOMETHING WITH CATEGORIES - Level 1
Category.push({Name: temp1, ID: ID2, ParentID: 'null', ChildrenIDs: Level2Children});
//FINISH
Though when it finishes, if I call Level1CatsLen it is not 16, which is the inital number it is now 33 which is the final stage.
Any ideas how one would go about doing this?
Updated answer:
There are a couple of issues here.
You have function declarations within control structures, which is a syntax error:
// ...
for (i = 0; i <= Level1CatsLen-1; i++) {
var ID1 = Level1Cats[i].id;
var temp1 = Level1Cats[i].innerHTML;
temp1.replace(/&/gi, "&").replace(/<[^>]*>/gi, "");
function GoToLevel2(i) { // <=== Error
dispatchMouseEvent(Level1Cats[i], "mouseover", true, true);
dispatchMouseEvent(Level1Cats[i], "click", true, true);
}
// ...
}
Browsers have a tendency to tolerate it, but the way they tolerate it varies from browser to browser. To create a function within a control structure, you need to use a function expression, not a function declaration:
var GoToLevel2 = function(i) {
dispatchMouseEvent(Level1Cats[i], "mouseover", true, true);
dispatchMouseEvent(Level1Cats[i], "click", true, true);
};
But see #2.
There's no need to recreate your functions on every loop, if you're going to pass i into them anyway.
Your setTimeout call is incorrect:
setTimeout(GoToLevel2(i),100);
That calls GoToLevel2, passing in i, and passes its return value into setTimeout, exactly the way foo(bar()) calls bar and passes its return value into foo.
To set up a timed callback to GoToLevel2, you use the function reference. To ensure that it receives a specific value (i), you can use Function#bind to get a new function that will call GoToLevel2 with that i:
setTimeout(GoToLevel2.bind(null, i),100);
It's possible there are further issues, those are the ones that jumped out at me.
Original answer:
You're doing that, on this line:
var Level1CatsLen = Level1Cats.length;
As the DOM changes, Level1Cats.length may change (because getElementsByClassName returns a live NodeList), but Level1CatsLen won't.
var Level1Cats = document.getElementsByClassName("p-pstctgry-lnk-ctgry");
var Level1CatsLen = Level1Cats.length;
snippet.log("Initial: Level1CatsLen = " + Level1CatsLen + ", Level1Cats.length = " + Level1Cats.length);
tick();
function tick() {
var div = document.createElement('div');
div.className = "p-pstctgry-lnk-ctgry";
div.innerHTML = String(Level1Cats.length + 1);
document.body.appendChild(div);
snippet.log("Updated: Level1CatsLen = " + Level1CatsLen + ", Level1Cats.length = " + Level1Cats.length);
if (Level1Cats.length < 10) {
setTimeout(tick, 500);
}
}
.p-pstctgry-lnk-ctgry {
border: 1px solid #888;
color: green;
}
<div class="p-pstctgry-lnk-ctgry">1</div>
<div class="p-pstctgry-lnk-ctgry">2</div>
<div class="p-pstctgry-lnk-ctgry">3</div>
<!-- Script provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>

How to find the number of form elements that are getting passed to e.parameter in GAS?

In Google App Scripts (GAS), I want to be able to add and remove TextBox and TextArea elements to a FlexTable (that's being used as a form) and not worry about how many there are. I've named the text elements based on a counter to make this process easier.
So, is there a way to get the number of inputs (TextBox + TextArea) passed to e.parameter after the form is submitted?
Here's the relevant code from the FlexTable:
function doGet() {
var app = UiApp.createApplication();
var flex = app.createFlexTable().setId('myFlex');
var counter = 0;
var row_counter = 0;
...
var firstnameLabel = app.createLabel('Your FIRST Name');
var firstnameTextBox = app.createTextBox().setWidth(sm_width).setName('input' + counter).setText(data[counter]);
flex.setWidget(row_counter, 1, firstnameLabel);
flex.setWidget(row_counter, 2, firstnameTextBox);
row_counter++;
counter++;
var lastnameLabel = app.createLabel('Your LAST Name');
var lastnameTextBox = app.createTextBox().setWidth(sm_width).setName('input' + counter).setText(data[counter]);
flex.setWidget(row_counter, 1, lastnameLabel);
flex.setWidget(row_counter, 2, lastnameTextBox);
row_counter++;
counter++;
...
var submitButton = app.createButton('Submit Proposal');
flex.setWidget(row_counter, 2, submitButton);
var handler = app.createServerClickHandler('saveProposal');
handler.addCallbackElement(flex);
submitButton.addClickHandler(handler);
var scroll = app.createScrollPanel().setSize('100%', '100%');
scroll.add(flex);
app.add(scroll);
return app;
}
And here's the code for the ClickHandler (notice that I currently have 39 elements in my FlexTable):
function saveProposal(e){
var app = UiApp.getActiveApplication();
var userData = [];
var counter = 39;
for(var i = 0; i < counter; i++) {
var input_name = 'input' + i;
userData[i] = e.parameter[input_name];
}
So, is there a way to get the number of elements (in this case 39) without manually counting them and assigning this value to a variable?
I'm new at this stuff and I'd appreciate your help.
Cheers!
The simplest way is to add a hidden widget in your doGet() function that will hold the counter value like this :
var hidden = app.createHidden('counterValue',counter);// don't forget to add this widget as a callBackElement to your handler variable (handler.addCallBackElement(hidden))
then in the handler function simply use
var counter = Number(e.parameter.counterValue);// because the returned value is actually a string, as almost any other widget...
If you want to see this value while debugging you can replace it momentarily with a textBox...
You can search for arguments array based object.
function foo(x) {
console.log(arguments.length); // This will print 7.
}
foo(1,2,3,4,5,6,7) // Sending 7 parameters to function.
You could use a while loop.
var i = 0;
var userData = [];
while (e.parameter['input' + i] != undefined) {
userData[i] = e.parameter['input' + i];
i++;
};
OR:
var i = 0;
var userData = [];
var input_name = 'input0';
while (e.parameter[input_name] != undefined) {
userData[i] = e.parameter[input_name];
i++;
input_name = 'input' + i;
};

Calling Multiple functions simultaneously

I'm trying to call two different functions for two different HTML elements at the same time, but the second function isn't being read at all. I'm also trying to use the id to specify which corresponding elements to grab data from. Here's what I have:
function changeImage(id)
{
var s = document.getElementById('showcase');
var simg = s.getElementsByTagName('img');
var slen = simg.length;
for(i=0; i < slen; i++)
{
simg[i].style.display = 'none';
}
$('#' + id).fadeIn('slow', 0);
function createComment(jim)
{
//alert('hello?');
var d = document.getElementById('description');
var dh = document.getElementsByTagName('p');
var dlen = dh.length;
//alert(dh);
for(i=0; i < dlen; i++)
{
alert(dh);
dh[i].style.display = 'none';
}
$('#' + jim).fadeIn('slow', 0);
}
It appears you are missing the closing bracket} at the end of your changeImage function.
Also you could shorten your script substantially using jQuery:
function changeImage(id)
{
$('#showcase img').hide();
$('#' + id).fadeIn('slow');
}
function createComment(jim)
{
$('#description p').hide();
$('#' + jim).fadeIn('slow');
}
Also, I'm not sure why you have a zero inside the fadeIn() function? If you want the img/p to show instantly, just use .show()

How to change a button from another function?

var ButtonFarmAtivada = new Array();
function X() {
var tableCol = dom.cn("td"); //cell 0
//create start checkbox button
ButtonFarmAtivada[index] = createInputButton("checkbox", index);
ButtonFarmAtivada[index].name = "buttonFarmAtivada_"+index;
ButtonFarmAtivada[index].checked = GM_getValue("farmAtivada_"+index, true);
FM_log(3,"checkboxFarm "+(index)+" = "+GM_getValue("farmAtivada_"+index));
ButtonFarmAtivada[index].addEventListener("click", function() {
rp_farmAtivada(index);
}, false);
tableCol.appendChild(ButtonFarmAtivada[i]);
tableRow.appendChild(tableCol); // add the cell
}
1) is it possible to create the button inside an array as I'm trying to do in that example? like an array of buttons?
2) I ask that because I will have to change this button later from another function, and I'm trying to do that like this (not working):
function rp_marcadesmarcaFarm(valor) {
var vListID = getAllVillageId().toString();
FM_log(4,"MarcaDesmarcaFarm + vListID="+vListID);
var attackList = vListID.split(",");
for (i = 0; i <= attackList.length; i++) {
FM_log(3, "Marca/desmarca = "+i+" "+buttonFarmAtivada[i].Checked);
ButtonFarmAtivada[i].Checked = valor;
};
};
For number 1) yes, you can.
function createInputButton(type, index) { // um, why the 'index' param?
// also, why is this function called 'createInputButton'
// if sometimes it returns a checkbox as opposed to a button?
var inputButton = document.createElement("input");
inputButton.type = type; // alternately you could use setAttribute like so:
// inputButton.setAttribute("type", type);
// it would be more XHTML-ish, ♪ if that's what you're into ♫
return inputButton;
}
I don't really understand part 2, sorry.

Categories