How do I make a JavaScript created list item clickable? - javascript

I've been going through all the related questions but none of the solutions have been working for me. I am extremely new to JavaScript and I'm confused as to how to make a list that I created with JavaScript have clickable items. The most recent attempt included attempting to make an alert pop up on click but instead it just pops up the second the page loads. Please help! Here is my current code:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="css/m-buttons.css">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
div.links{
margin: auto;
border: 3px solid #003366;
text-align: left;
max-width: 700px;
}
p{
font-size: 40px;
text-align: center;
}
li{
font-size: 1w;
}
body{
font-family: verdana;
}
</style>
</head>
<body>
<div class = "links">
<ul id="blah"></ul>
<script>
var testArray = ["One","Two","Three","Four","Five","Six","Seven"];
function makeLongArray(array){
for(var i = 0; i < 1000; i++){
array.push(i);
}
}
makeLongArray(testArray);
function makeUL(array) {
// Create the list element:
var list = document.createElement("UL");
list.setAttribute("id", "blah");
for(var i = 0; i < array.length; i++) {
// Create the list item:
var item = document.createElement("LI");
// Set its contents:
item.appendChild(document.createTextNode(array[i]));
// Add it to the list:
list.appendChild(item);
list.onclick = alert("Help."); //this is the code causing the issue.
}
// Finally, return the constructed list:
return list;
}
// Add the contents of options[0] to #foo:
document.getElementById("blah").appendChild(makeUL(testArray));
</script>
</div>
</body>
</html>

Your existing code will execute alert('Help.') every time you execute this line of code list.onclick = alert("Help.");
What you need to do is assign a function to onclick. This function then get executed when onclick is executed. As follows:
item.onclick = function() {console.log('hello world');};
Now each list item's onclick event has a function assigned to it that outputs hello world to the console everytime the list item is clicked.

You need to assign a function to the onclick event:
list.onclick = function(){ alert("Help."); }

Here is my solution:
function toggleDone(event) {
var ev = event.target;
ev.classList.toggle("done");
}
ul.addEventListener("click", toggleDone);

Related

JS: calling functions via buttons does not work anymore, if page is updated without reloading the page

I want to write a program with one html page, which i can update and fill with different elements via javascript with one button that stays the same in every version, which displays a modalBox. I made a very basic version of this: One page, that is filled with two buttons (next and last) for navigating through the pages and one to display the modal. In addition, i added a number, which is incremented oder decremented accordingly, when you click through the updated versions of the page.
var counter = 1;
function setUp(){
var c = document.getElementById("container");
var d = document.createElement("div");
d.setAttribute("id", "main");
d.innerHTML = counter;
var nxt = document.createElement("button");
var bck = document.createElement("button");
var modalBtn = document.createElement("button");
nxt.innerText = ">";
bck.innerText = "<";
modalBtn.innerText="Show Modal";
nxt.setAttribute("onclick","nextPage()");
bck.setAttribute("onclick","lastPage()");
modalBtn.setAttribute("onclick","showModal()");
d.appendChild(bck);
c.appendChild(d);
d.appendChild(nxt);
d.appendChild(modalBtn);
}
function showModal(){
var m = document.getElementById("modal");
m.style.display = "block";
}
function closeModal(){
var m = document.getElementById("modal");
m.style.display = "none";
}
function nextPage(){
var c = document.getElementById("container");
c.innerHTML="";
counter++;
setUp();
}
function lastPage(){
var c = document.getElementById("container");
c.innerHTML="";
counter--;
setUp();
}
setUp();
*{
padding: 0;
margin: 0;
}
#modal{
position: absolute;
width: 500px;
height: 300px;
background-color: black;
display: none;
}
#main{
background-color: aliceblue;
height: 500px;
width: 800px;
}
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="tryout.css">
<script src="tryout.js" defer></script>
<title>Document</title>
</head>
<body>
<div id="container">
<div id="modal"><button id="closeButton" onclick="closeModal()">Close</button></div>
</div>
</body>
</html>
The problem is: on onload, the modal button works fine (on click, the modal is displayed). As soon as i update (not reloading!) the page via the next- or back button, the modal button stops working (Error-message says the type of modalbutton is null). I have no clue why, because to my knowledge, the buttons are reinitiated by clicking on the next or back button (because the setUp()-function is called in the functions triggered by the buttons). As soon as I reload the page via the reload-button, it is working until i use one of the next and back buttons.
I am new to js, it's probable that I'm missing sth. obvious here :) Many Thanks!
On the "nextPage" and "lastPage" function , you're just removing the whole content (including the modal) from the div which associated container class.
That's why when you calling the "showModal" function , there is no element with modal id on the DOM. That's why its saying null.
you can follow what Sakil said on the comment or, in your both(nextPage & lastPage) functions,you can just remove the div with id main and add it later on "setUp" function.
I'm adding some code snippet below, hope it will help;
function nextPage() {
//grab the div with "main" id
let main = document.getElementById("main");
//remove it from document
main.remove();
counter++;
//add it again; what you're doing actually.
setUp();
}
function lastPage() {
//grab the div with "main" id
let main = document.getElementById("main");
//remove it from document
main.remove();
counter--;
//add it again; what you're doing actually.
setUp();
}
of course you should refactor the code base, cause there is lot more copy-pasting staff present, but I'm leaving that up to you.

javascript unbind event not unbinding even though it fired

I have some code that when called inside an object, binds an element on the page to a click. When I press a start button again, the elements are updated and I'm adding an event listener for click again. I'm unbinding the event listener prior to adding the even listener again, it fires 10 times to unbind... and then I bind, and it fires 10 times for the binding to be readded... BUT. when I inspect the element, or click the element, it fires twice. If I hit start again, it now fires three times...
card.clickFn = function() {
if(Game.cardsClickedHistory.length<2 && Game.status==1){
card.element.style.backgroundImage = card.image;
Game.cardClicked(card); // notify a card clicked
}
}
card.element.removeEventListener("click", card.clickFn);
card.element.addEventListener("click", card.clickFn);
Repeatable examples
<!DOCTYPE html>
<html>
<head>
<title></title>
<style>
.card {width: 200px; height: 200px; background: red; margin: 10px;}
</style>
</head>
<body>
<div id="cards">
<div class="card"></div>
<div class="card"></div>
</div>
<button id="rebind">REBIND</button>
<script>
var Card = function(element){
var card = this;
card.element = element;
card.clickFn = function(){
console.log("Clicked card");
}
card.element.removeEventListener("click", card.clickFn);
card.element.addEventListener("click", card.clickFn);
}
var CardFactory = function()
{
var cf = this;
cf.cardDivs = document.getElementsByClassName("card");
cf.build = function(){
for(var i=0;i<cf.cardDivs.length; i++)
{
new Card(cf.cardDivs[i]);
}
}
}
var oCF = new CardFactory();
oCF.build();
document.getElementById("rebind").addEventListener("click", oCF.build)
</script>
</body>
</html>
When you instantiate Card object by calling constructor new Card(cf.cardDivs[i]); , the constructor create new function that is not previous function in last construction. Therefore when it tries to remove listener, obviously can not find the previous listener. To Avoid this, you could set a static variable (listeners) that stores bound listeners. Then on next cal of build() function easily can find exact recorded previous bound function as listener. I added a little bit features in your code that it works.
<!DOCTYPE html>
<html>
<head>
<title></title>
<style>
.card {width: 200px; height: 200px; background: red; margin: 10px;}
</style>
</head>
<body>
<div id="cards">
<div class="card"></div>
<div class="card"></div>
</div>
<button id="rebind">REBIND</button>
<script>
var Card = function(element){
var card = this;
card.element = element;
card.clickFn = function(){
console.log("Clicked card");
}
var listener=Card.listeners.filter(function(lis){ return lis.ele===element })[0];
if(listener!==undefined){
card.element.removeEventListener("click", listener.func);
Card.listeners=Card.listeners.filter(function(lis){return lis.ele!==element}); // remove previous listener record
}
card.element.addEventListener("click", card.clickFn);
Card.listeners.push({ele:element,func:card.clickFn}); // add record of listener
}
Card.listeners=[]; //static member
var CardFactory = function()
{
var cf = this;
cf.cardDivs = document.getElementsByClassName("card");
cf.build = function(){
for(var i=0;i<cf.cardDivs.length; i++)
{
new Card(cf.cardDivs[i]);
}
}
}
var oCF = new CardFactory();
oCF.build();
document.getElementById("rebind").addEventListener("click", oCF.build)
</script>
</body>
</html>

Can I use function generator as event handler?

I have a list of HTMLCollection:
<div class="demo">Access me by class[1]</div>
<div class="demo">Access me by class[2]</div>
<div class="demo">Access me by class[3]</div>
<div class="demo">Access me by class[4]</div>
And I have a script of JS:
var getElements = document.getElementsByClassName("demo");
const generatorObject = generatorFunction();
function* generatorFunction(e) {
for (i = 0; i < getElements.length; i++) {
yield getElements[i];
}
}
generatorObject.next(); // each time pressed a key down will invoke this line
// after invoking 6th time it will give {value: undefined, done: done}
My goal is to write a keyboardEvent based on .addEventListener("keydown", generatorFunction) method whereby event handler would be presented as function generator i.e. generatorFunction presented above. Is it good or bad practice?
Using a generator function directly as an event callback wouldn't make any sense because calling the function wouldn't execute its body, it would generate and return (to nowhere) an iterator.
Instead you'd need to wrap it in another function which talks to the iterator. I'm not sure exactly what your particular use case is (you talk about keydown events, but don't say on what element). But here's a general setup - in this example I'm yielding a number from an array on each keypress. On the final number, done is set to true.
function* generator() {
let toYield = [1, 2, 3, 4];
for (let i=0; i<toYield.length-1; i++) yield toYield[i];
return toYield.pop(); //return final item in array, which sets iterator to done
}
const iterator = generator();
document.querySelector('input').addEventListener('keydown', evt => {
let yielded = iterator.next();
console.log(yielded);
});
Fiddle
Did a bit of coding with reference which I am very thankful for #Mitya
var toYield;
function* generator() {
toYield = []; // five iterable yieldedObject(s) will be pushed
var getElements = document.getElementsByClassName("demo");
for (let i=0; i<getElements.length; i++)
yield toYield.push(getElements[i]);
}
const iterator = generator();
var yieldedObject;
window.addEventListener('keydown', ()=> {
yieldedObject = iterator.next();
console.log(yieldedObject); // yieldedObject has value:xyz/undefined and flag: false/done
colorizeIt();
});
function colorizeIt() {
for (const items of toYield){
switch(true) {
case yieldedObject.value >= 0:
items.style.backgroundColor = "yellow";
break;
default: items.style.backgroundColor = "white";
}
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Accessing Elements in the DOM</title>
<style>
html { font-family: sans-serif; color: #333; }
body { max-width: 500px; margin: 0 auto; padding: 0 15px; }
div { padding: 10px; margin: 5px; border: 1px solid #5e41ff; }
</style>
</head>
<body>
<h1>Simple keyboard event via event generator</h1>
<div class="demo">Access me by class[0]</div>
<div class="demo">Access me by class[1]</div>
<div class="demo">Access me by class[2]</div>
<div class="demo">Access me by class[3]</div>
<div class="demo">Access me by class[4]</div>
</body>
</html>
All worked just fine! Thanks a lot!

Javascript loading speed issue as variable increases

I am having an issue with page loading time. Currently right now I am running UBUNTU in Oracle Vm Virtual Box. I am using mozilla firefox as my browser and I am working on an etchasketch project from "The odin project".
My problem is the page loading time. The code takes a prompt at the start and generates a grid for the etch a sketch based on that prompt. I have not given it the minimum and maximum values (16 and 64) respectively, however any number when prompted at the beginning that is beyond 35 doesn't load or takes ages to load.
How do I speed up the process time? / why is it moving so slow? / how can I avoid this ? / is there a fix that I am over looking that can make this work a lot faster? / feel free to tackle any and all of those questions!
This is my HTML CODE:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" type="text/css" href="style.css">
<meta charset="utf-8"/>
<title>
</title>
</head>
<body>
<div class="etchhead">
<p> Choose your grid size </p>
<input type = "text"></input>
<button id="startOver"> Clear Grid </button>
<p> Change color </p>
</div>
<div id="grid">
</div>
<script src="eas.js"></script>
</body>
</html>
And this is my CSS code:
p {
color: blue;
display: inline;
}
#grid {
display: grid;
width: 800px;
max-width: 800px;
height: 800px;
max-height: 800px;
line-height: 0;
}
.gridBox {
border: 1px solid black;
background-color: lightgrey
}
And this is my JAVASCRIPT code:
gridStart();
function gridStart(){
var boxes = 0
var selectBody = document.querySelector("#grid");
var addBox = document.createElement("div");
var boxCountStart = prompt("enter a number between 16 and 64");
var boxDimensions = (boxCountStart * boxCountStart);
function rowsAndColumns() {
var selectBody = document.querySelector("#grid");
var gridTemplateColumns = 'repeat('+boxCountStart+', 1fr)';
selectBody.style.gridTemplateColumns= gridTemplateColumns;
selectBody.style.gridTemplateRows= gridTemplateColumns;
};
function hoverColor(){
var divSelector = selectBody.querySelectorAll("div");
divSelector.forEach((div) => {
div.addEventListener("mouseover", (event) => {
event.target.style.backgroundColor = "grey";
});
});
};
rowsAndColumns();
for (boxes = 0; boxes < boxDimensions ; boxes++) {
var selectBody = document.querySelector("#grid");
var addBox = document.createElement("div");
addBox.classList.add("gridBox");
addBox.textContent = (" ");
selectBody.appendChild(addBox);
hoverColor();
};
};
There are two components to your issue. One is that you are repeatedly modifying the DOM in a loop. You can fix it by appending all your boxes to a DocumentFragment and then adding that to the DOM after your loop finishes. You are also calling hoverColor(); inside your loop which results in adding tons of event listeners that all do the same thing (since inside hoverColor you are adding a listener to every single div). You can fix both those issues like this:
var fragment = document.createDocumentFragment( );
for (var i = 0; i < boxDimensions ; i++) {
var addBox = document.createElement("div");
addBox.classList.add("gridBox");
addBox.textContent = (" ");
fragment.appendChild(addBox);
}
document.querySelector("#grid").appendChild( fragment );
hoverColor();
Here is a JSFiddle with your original code, and here is one with the modification.
You could also benefit from only having one event listener total. You don't need to loop and add an event listener to every div. Just add one to #grid and use event.target (like you already do, to find the div that the event originated from). Something like this:
function hoverColor(){
document.querySelector("#grid").addEventListener( 'mouseover', function ( event ) {
event.target.style.backgroundColor = "grey";
} );
}

google closure tool combo box get model

How to get object from select combomenuitem...?i tried e.target.getMenu().getModel()
</html>
<head>
<title>goog.ui.ComboBox</title>
<script src="closure-library/closure/goog/base.js"></script>
<script>
goog.require('goog.events');
goog.require('goog.ui.ComboBox');
goog.require('goog.dispose');
goog.require('goog.dom');
var test;
</script>
<style>
html, body {
overflow:hidden;
margin: 0;
padding: 5px;
}
#c {
margin-bottom: 10px;
font-size: small;
}
/* Size the combobox so that it is sufficiently small to demonstrate the menu
being positioned to left-align with the control. */
.goog-combobox input {
width: 100px;
}
fieldset {
display: inline-block;
margin: 10px;
text-align: initial;
}
</style>
</head>
<body>
<div id="c">cb.value = '<span id="v"></span>'</div>
<fieldset style="float:left">
<legend>LTR</legend>
<div class="combo"></div>
</fieldset>
<script type="text/javascript">
function createTestComboBox() {
var cb = new goog.ui.ComboBox();
cb.setUseDropdownArrow(true);
cb.setDefaultText('Select a folder...');
cb.addItem(new goog.ui.ComboBoxItem('Inbox',{"a":1}));
cb.addItem(new goog.ui.ComboBoxItem('Bills & statements',{"b":2}));
cb.addItem(new goog.ui.ComboBoxItem('Cal alumni',{"c":3}));
cb.addItem(new goog.ui.ComboBoxItem('Calendar Stuff',{"d":4}));
cb.addItem(new goog.ui.ComboBoxItem('Design',{"e":5}));
cb.addItem(new goog.ui.ComboBoxItem('Music',{"f":6}));
cb.addItem(new goog.ui.ComboBoxItem('Netflix',{"g":7}));
cb.addItem(new goog.ui.ComboBoxItem('Personal',{"h":8}));
cb.addItem(new goog.ui.ComboBoxItem('Photos',{"i":9}));
cb.addItem(new goog.ui.ComboBoxItem('Programming languages',{"j":10}));
return cb;
}
var controls = [];
var containerEls = goog.dom.getElementsByClass(goog.getCssName('combo'));
for (var i = 0; i < containerEls.length; i++) {
var cb = createTestComboBox();
cb.render(containerEls[i]);
goog.events.listen(cb, 'change', handleChangeEvent);
controls.push(cb);
}
function handleChangeEvent(e) {
test=e;
goog.dom.setTextContent(document.getElementById('v'), e.target.getValue());
}
window.onbeforeunload = function() {
goog.disposeAll(controls);
};
</script>
</body>
</html>
You can use getModel which is defined in goog.ui.component and it should be available to ComboBoxitem thru Prototype Inheritance
goog.Disposable >> goog.events.EventTarget >> goog.ui.Component >> goog.ui.Control >> goog.ui.MenuItem >> goog.ui.ComboBoxItem
goog.ui.Component.prototype.getModel = function() {
return this.model_;
};
https://code.google.com/p/closure-library/source/browse/closure/goog/ui/component.js
Documentation - http://docs.closure-library.googlecode.com/git/class_goog_ui_ComboBoxItem.html [ find GetModel ]
In the example code the event listener is attached to the ComboBox, can't find any method of getting the currently selected ComboBoxItem and that's were the model was attached to. So the question is: "How to get access to the ComboBoxItem when attaching change event on the Combobox?" I would expect index or selectedItemIndex or something like that.
goog.events.listen(cb, 'change', handleChangeEvent);
// inside event handler
e.target===this;//true
//second item was clicked:
this === cb;//true
cb.getItemAt(1).getValue();//prints the model object
// but ComboBox has no selectedItemIndex
// or something helpful to get the ComboBoxItem
It would be helpful if they could provide a working example in either the demo's or unit tests, more people would be able to use it. As it is now it's very hard to figure out this mostly undocumented library.

Categories