JavaScript: Adding onClick to Div with URL as a Variable - javascript

The code below makes a Div for every object it finds in an array. It then adds a header, a paragraph, an image, and an anchor.
The div's actually look like rectangles on top of one another. What I would like to do is add an onclick attribute to the divs based on the keys within my objects ( coffeShops[i].menu). When I do so though, clicking just doesn't do anything and i get this error message in console
"Uncaught TypeError: Cannot read property 'menu' of undefined"
This only happens when I try window.open with the object key coffeShops[i].menu. If I swap that out with "http://www.google.com" it works just fine. Also, the links from that variable show up in the console just fine. So I know it's getting the data from the object just fine. But for some reason it doesn't want to open from the div.
FYI I'm extremely new to this so apologies if the explanation doesn't make sense.
var containerDiv = document.getElementById("container");
console.log(containerDiv);
for (var i = 0; i < coffeeShops.length; i++){
var launchMenu = function(){
window.open(coffeeShops[i].menu);
}
console.log(coffeeShops[i].menu);
var coffeeShopDiv = document.createElement("div");
coffeeShopDiv.className = "coffeeShop";
coffeeShopDiv.onclick = launchMenu;
containerDiv.appendChild(coffeeShopDiv);
var coffeeShopImage = document.createElement("img");
coffeeShopImage.src = coffeeShops[i].image;
coffeeShopImage.className = "coffeeImage";
coffeeShopDiv.appendChild(coffeeShopImage);
var coffeeShopHeader = document.createElement("h1");
coffeeShopHeader.className = "coffeeHeader"
coffeeShopHeader.innerHTML = coffeeShops[i].name;
coffeeShopDiv.appendChild(coffeeShopHeader);
var coffeeShopPar = document.createElement("p");
coffeeShopPar.className = "coffeeDescription"
coffeeShopPar.innerHTML = coffeeShops[i].description;
coffeeShopDiv.appendChild(coffeeShopPar);
var coffeeMenu = document.createElement("a");
coffeeMenu.href = coffeeShops[i].menu;
coffeeMenu.innerHTML = "MENU"
coffeeShopDiv.appendChild(coffeeMenu);
};

EDITED: in light of comment
It looks like you need to separate the function which opens the new window from the code which generates page items. By slightly restructuring your code this can be achieved by using the element.addEventListener() method.
/* place the 'launchMenu' outside of
the for-loop so it can be accessed
by each of the target elements */
function launchMenu(event){
/* get the class attribte of
the clicked element */
var clickedClass = event.target.className;
var targetElement,
link;
/* is the clicked element the containing div? */
if (clickClass !== 'coffeeShop') {
/* nope */
targetElement = event.target.parentNode;
} else {
/* yep */
targetElement = event.target;
}
/* get 'data-url' of target */
link = targetElement.getAttribute('data-url');
/* open the new window */
window.open(link);
}
/* elsewhere: create elements as before */
for (var i = 0; i < coffeeShops.length; i++){
var coffeeShopDiv = document.createElement("div");
coffeeShopDiv.className = "coffeeShop";
/* add 'data-' attribute to target div */
coffeeShopDiv.setAttribute('data-url', coffeeShops[i].menu);
containerDiv.appendChild(coffeeShopDiv);
var coffeeShopImage = document.createElement("img");
coffeeShopImage.src = coffeeShops[i].image;
coffeeShopImage.className = "coffeeImage";
coffeeShopDiv.appendChild(coffeeShopImage);
var coffeeShopHeader = document.createElement("h1");
coffeeShopHeader.className = "coffeeHeader"
coffeeShopHeader.innerHTML = coffeeShops[i].name;
coffeeShopDiv.appendChild(coffeeShopHeader);
var coffeeShopPar = document.createElement("p");
coffeeShopPar.className = "coffeeDescription"
coffeeShopPar.innerHTML = coffeeShops[i].description;
coffeeShopDiv.appendChild(coffeeShopPar);
var coffeeMenu = document.createElement("a");
coffeeMenu.className = 'coffeeMenu';
coffeeMenu.innerHTML = "MENU"
coffeeShopDiv.appendChild(coffeeMenu);
/* attach an eventListener to each
created '.coffeeShopDiv' element */
coffeeMenu.addEventListener(
'click', launchMenu, false
);
};
The target element (coffeeShopDiv) now contains a url-reference in its 'data-url' attriubte and has an event listener assigned.
When the target element, or any of her child elements, are clicked the launchMenu() function will be invoked. This function checks which element was clicked by comparing the className attribute to the class name of 'coffeeShopDiv' - such that if the class name of the clicked element is not 'coffeeShop' the targetElement variable is assigned to the clicked elements parent Node (that is, 'coffeShopDiv').
The 'data-url' reference is obtained and used in window.open().
Also see:
Using data-* attributes
Event.target
Quriksmode: Introduction to Events
This StackOverflow answer about using
window.open()

Related

Adding events and functions on elements generated with createElement()

I created different elements-paragraphs with createElement()/createTextNode() and added them to the body.
My problem is that i want to make those divs links or be able to add events such as onclick and obviously there is no HTML code to do that..just javascript generated objects.
my code atm:
for (i=0; i<10; i++){
var newDiv = document.createElement("div");
newDiv.className = "block";
var heading = document.createElement("h2");
var newContent = document.createTextNode(data[1][i]);
heading.className="title";
heading.appendChild(newContent);
newDiv.appendChild(heading);
var paragraph = document.createElement("p");
var newContent2 = document.createTextNode(data[2][i]);
paragraph.className="light";
paragraph.appendChild(newContent2);
newDiv.appendChild(paragraph);
var currentDiv = document.getElementById("div1");
document.body.insertBefore(newDiv, currentDiv);
}
You can add the event listener to the object you just created. The object does not have to be HTML. Read more about adding event listeners and see simple example:
var someDiv = document.createElement('div');
var txt = document.createTextNode('click me');
someDiv.append(txt);
document.body.append(someDiv);
var myFancyFunction = function() {
alert('you clicked me');
};
someDiv.addEventListener('click', myFancyFunction);
Update after your code you can add an event listener to those objects you create on the fly. You can also add different functions on the same event. In this case it's the same function for both elements/objects .. play with this: (I changed the data to "dummy data" as there was no data)
var myClick = function(event) {
alert(event.target.innerHTML);
};
for (i=0; i<10; i++){
var newDiv = document.createElement("div");
newDiv.className = "block";
var heading = document.createElement("h2");
var newContent = document.createTextNode('dummy data1 index: ' + i);
heading.className="title";
heading.appendChild(newContent);
newDiv.appendChild(heading);
var paragraph = document.createElement("p");
var newContent2 = document.createTextNode('dummy data2 index: ' + i);
paragraph.className="light";
paragraph.appendChild(newContent2);
newDiv.appendChild(paragraph);
var currentDiv = document.getElementById("div1");
document.body.insertBefore(newDiv, currentDiv);
heading.addEventListener('click', myClick);
paragraph.addEventListener('click', myClick);
}
You can simply call addEventListener on the JS-generated objects, even before they are inserted into the DOM, or are never inserted at all:
let div = document.createElement('div');
div.addEventListener('click', function(event) {
// do something
});
// This will trigger a call of the registered click callback,
// regardless of whether the div is in the DOM:
div.dispatchEvent(new Event('click', {
"bubbles": true,
"cancelable": false,
}));
// To add it to the DOM, simply add it the way you wish:
document.body.appendChild(div);
EventTarget.dispatchEvent allows you to trigger an event in a computational way (has equivalent effect to actually clicking the div)
Do take note of the browser compatibility of the event constructor in the example above.
Once an element is added to the dom, you can select it just like any other element.
// Create the element
var paragraph = document.createElement('p');
// Give it an `id`
paragraph.id = 'foo';
// Add the element to the `dom`
document.body.appendChild(paragraph);
// Add the listener
paragraph.addEventListener('click', function() {
this.innerHTML = 'It was clicked';
});
p {
height:20px;
padding:10px;
outline:1px solid #bada55;
}
In the example above, I added an id. If for some reason you need to re-select the element it may make it easier.

Why does this keep reading null rather than setting my div to different id's

So basically we're trying to make a pixel art program where you assign "pixels" to different div colors. I keep returning id's rather than resetting them, even if they're null. We're supposed to be doing this in vanilla javascript even though jQuery would make it significantly easier
document.addEventListener("DOMContentLoaded", () => {
console.log("dom content loaded");
//sets up div for loop.
function divAdder() {
var pixelContainer = document.createElement("div");
pixelContainer.setAttribute("class", "pixelContainer");
document.body.appendChild(pixelContainer);
//creates and numbers divs
for (var i = 0; i < 1073; i++) {
var id = "Div ID #";
var element = document.createElement("div");
element.setAttribute("class", "pixelClass");
document.addEventListener("click",colorPicker);
document.addEventListener("click",colorSet);
pixelContainer.appendChild(element);
}
}
divAdder()
var currentColor = undefined
function colorPicker(event) {
currentColor = event.target.getAttribute("id")
console.log("currentColor = " + currentColor)
}
function colorSet(event){
console.log( "colorSet = " + currentColor)
event.target.setAttribute("id", currentColor)
};
At JavaScript at Question the event is attached to document, not the created element element. event.target within click handler is document, not element. No id is set at element. Note also id of element in document should be unique.

Change InnerHTML based on anchor click

I'm a little new to Web Development so I was hoping someone could answer this for me.
I'm building a prototype for a "Web Messenger" similar to Facebook messenger. I have a sidebar that I populate with a UL of anchor tags when the window loads and it looks like this. Here is the code for it
var toAdd = document.createDocumentFragment();
var newUL = document.createElement('ul');
newUL.id = 'menu-content';
newUL.className = 'menu-content collapse out';
for(var i = 0; i < 5; i++){
var newLI = document.createElement('li');
var newA = document.createElement('a');
newA.id = 'chat' + i;
newA.setAttribute('href', "#");
newA.innerHTML = "Chat" + (i + 1);
newLI.appendChild(newA);
newUL.appendChild(newLI);
}
toAdd.appendChild(newUL)
document.getElementById("menu-list").appendChild(toAdd);
I also have a Div at the top of the page which will display some details about the current chat, but for the time being will simply display the name of the chat, same as on the anchor tags.
Now based on another StackOverflow post the correct way to call a JS function from an anchor tag is
var c0 = document.getElementById("chat0");
//Set code to run when the link is clicked
// by assigning a function to "onclick"
c0.onclick = function(id) {
//Change Title Name here
//Do other stuff in the future
return false;
}
However I could have 20+ chats on the sidebar at any one time, so this seems like a lot of repetitive code to write.
Is there a better way to do this?
Give your chats a general class instead example chat then attach the click event to all the chat's in the same time using .getElementsByClassName().
So you could add className just after newA.id :
newA.id = 'chat' + i;
newA.className = 'chat';
Then attach the click event :
var chats = document.getElementsByClassName('chat');
for (var i=0; i < chats.length; i++) {
chats[i].addEventListener('click', chatClick, false);
}
function chatClick(){
//chat is clicked
//The 'this' object here refer to clicked element
}
You could use .addEventListener() instead of onclick.
NOTE : You can attach the click event directly after node creation :
for(var i = 0; i < 5; i++){
var newLI = document.createElement('li');
var newA = document.createElement('a');
newA.id = 'chat' + i;
newA.setAttribute('href', "#");
newA.innerHTML = "Chat" + (i + 1);
newA.addEventListener('click', chatClick, false); //Attach click event HERE
newLI.appendChild(newA);
newUL.appendChild(newLI);
}
Hope this help.
Yap sure. You can give them all a class and after load just use a simple jQuery code to bind onclick for all of them:
$('a.className').click(function() {
// Your code here
});
Now if the a tags are added after execution of the code you just simply need to call this again or bind to those elements manually.

Div-Element don't refresh after dynamic adding of elements

I'm working on an Ionic-app.
On one of my pages i'm adding elements dynamically to a div-element.
The elements are added but i can't see them on the page.
Just after clicking F5 and running the controller-code again i can see them.
Here's the code for adding the elements:
if ($scope.eigenschaften != null) {
//TODO Eigenschaften in Loop durchgehen und auf panel_dynamic Controls erzeugen
var panel_dynamic = document.getElementById('panel_dynamic');
if (panel_dynamic.hasChildNodes()) {
panel_dynamic.removeChild(panel_dynamic.childNodes[0]);
}
var content = document.createElement('div');
for (n = 0; n <= $scope.eigenschaften.length - 1; n++) {
// Creates a new div with controls
var line = document.createElement('div');
var para = document.createElement('p');
// Creates a div-row
var div_row = document.createElement('div');
div_row.setAttribute('class', 'row');
// Creates a div-col with label
var div_col_label = document.createElement('div');
div_col_label.setAttribute('class', 'col');
div_col_label.appendChild(CreateLabel('font-size:16px;font-weight:bold;', $scope.eigenschaften[n].name));
div_row.appendChild(div_col_label);
// Creates a div-col with control
var div_col_control = document.createElement('div');
div_col_control.setAttribute('class', 'col');
div_col_control.appendChild(CreateTextbox());
div_row.appendChild(div_col_control);
// Adds the div-row to the para
para.appendChild(div_row);
// Adds the new para to the line
line.appendChild(para);
// Adds the new line to the content
content.appendChild(line);
}
// Adds the content-div to panel_dynamic
panel_dynamic.appendChild(content);
}
"eigenschaften" is an array with the data.
It looks like you aren't triggering change detection. You should add the elements using an ng-repeat in your template that's bound to an array or some other iterable on the model instead. Then everytime you add something new to the iterable, it will get added in the view.
Unfortunately i used the id for my container-div "panel_dynamic" even in another template. And JavaScript was getting the other div by getElementById. Now i have changed the ids and it works.

How to remove default document style from an element using Javascript

I am trying to create a div element which shows some text as popup. But the problem is when i create the element using javascript, the div already contains the default style which is defined in document style sheet or external css. Like for example:
popup = document.createElement("div");
popup.setAttribute("id","myElement");
popup.style.left = "100px";
popup.style.top = "100px";
popup.style.width = "100px";
popup.style.height = "100px";
document.body.appendChild(popup);
and now when it shows up, it already has colors, borders etc because in css there is this div style which is being applied on this element. I want it to not inherit anything which is defined for the document itself, how can i do that, or may be overwrite the original style??
A way to achieve this is to override all of the available properties set with the css in this document. But with JavaScript is a lot of work.
Better way is to add an id or class to the newly created element. The style should have all of the properties that are applicable for the type of element you are creating. If you miss a property it will be set from the present css.
I would give these new divs a specific class name and link a custom stylesheet that resets all properties for that class to the desired defaults (ensuring that the linked styles have the necessary precedence in the cascade). Moving this reset into your JavaScript sounds like a bad idea.
So I made something like this. It may not be ideal solution but it atleast does what i wanted:
function displayMenu(){
var popup;
//see if there is already default style defined in the document
var styleSheets = document.styleSheets;
var size = styleSheets.length;
var cssRules = new Array();
var rules = "";
var css, len, st, sp;
for (i=0;i<size;i++){
cssRules[i] = styleSheets[i].rules || styleSheets[i].cssRules;
for (j=0;j<cssRules[i].length;j++){
if(cssRules[i][j].cssText.search(/div/i) != -1){
css = cssRules[i][j].cssText;
css = css.substr(((css.search("{"))+1),((css.search("}"))-(((css.search("{"))+1))));
if((css.search("{") == -1) && (css.search("}") == -1)) {
//no of css-properties in this specific rule
len = css.split(";").length - 1;
for (k=0;k<len;k++){
st = css.search(";") + 1;
rules += css.substr(0,(css.substr(0,st).search(":")+1)) + "0\n";
css = css.substr(st);
}
} else {} //ignore this rule
}
}
}
var reset = '.myStyle { '+ rules +' }\n';
//now create a css Class which overwrite default document properties for this <div> element
var myStyle = document.createElement('style');
myStyle.type = 'text/css';
//TODO: should be replaced with style from arguments
myStyle.innerHTML = reset;
document.getElementsByTagName('head')[0].appendChild(myStyle);
//start creating the popup menu:
var popup;
popup = document.createElement("div");
popup.setAttribute("id","guide_popup");
popup.setAttribute("class","myStyle");
//now define own style rules: (All basic properties need to be defined as there is none by defualt now)
popup.style.top = top;
popup.style.left = left;
popup.style.width = width;
popup.style.height = height;
popup.style.zIndex = index;
//TODO: should be replaced with str in aruguments
var popup_text = document.createTextNode("This is my sample text");
popup.appendChild(popup_text);
//finally process the DOM
document.body.appendChild(popup);
}

Categories

Resources