Adding a class to a created element - javascript

The parameter of my function is a function. It should create an element but I should still be able to add attributes by using the parameter details.
E.g.:
const addElement = (details) => {
const element = document.createElement('div');
}
addElement(function() {
element.id = 'my-div'; // Not working since element is not defined
});
Well, I have tried to store the element in an object to be able to use it outside of that function.
let element = {};
const displayVideo = (type, details) => {
element = document.createElement(type);
element.width = 200;
element.height = 200;
element.classList.add('my-class'); // <--- THE PROBLEM!
if (details) {
details();
}
document.querySelector('#layer').insertBefore(element, document.querySelector('#el'));
};
displayVideo('VIDEO', function () {
element.controls = true;
});
My element can not be created because of element.classList.add('my-class'); and I don't even get an error message. If I remove that line, it works but I would still like to be able to add a class to that object. How can I do this?

Just pass element into the function. Since you're just editing properties on the object, this won't cause reference vs value errors.
const addElement = (details) => {
const element = document.createElement('div');
if (details) details(element);
return element;
}
const ele = addElement(function(element) {
element.id = 'my-div';
});
console.log(ele);

In this case details could be something like classname.
function element(type, classname) {
var element = document.createElement(type);
if (classname !== undefined) {
element.classList.add(classname);
}
return element;
};
element("div","my-class"); //<div class="my-class"></div>
Of course instead of classname you could use an array or an object and loop through in order to set multiple attributes.
Or you could store the return value of your function in a variable and then add all the attributes:
var myelement = element("div");
myelement.classList.add("my-new-class");
myelement //<div class=​"my-new-class">​</div>​

Related

I have a problem with 2 Classes to create element on page and display it

So I made 2 classes,first is the create element one:
class CElement {
constructor(value,elementType,elementStyle){
this.element = document.createElement(elementType);
this.element.innerHTML = value;
this.element.style = elementStyle;
}
display(displayTo) {
document.getElementsByClassName(displayTo).appendChild(element);
}
}
The second is the create mob and diplay to:
class Mob {
constructor(level) {
this.mobLvl = level;
}
display(elementClass) {
ele = new CElement(this.mobLvl + "<br>",'p',"color:red;");
ele.display(elementId);
}
}
I checked my code online for syntax errors , and I don't have any?????
So why doesn't it work when I call:
var mob = new Mob(1,"div","color:red;");
mob.display("someClassName");
You have a couple of issues in your code:
You forgot this in the display() function.
getElementsByClassName() returns an array, so you can't use appendChild() directly, you have to loop over your array or use another selector, for example querySelector().
ele is not defined in your display() function.
You use elementClass as argument of the display() function but then use elementId inside your function.
Finally, a working version could be something like this, that you can of course adapt to your need:
class CElement {
constructor(value, elementType, elementStyle) {
this.element = document.createElement(elementType);
this.element.innerHTML = value;
this.element.style = elementStyle;
}
display(displayTo) {
document.querySelector(displayTo).appendChild(this.element);
}
}
class Mob {
constructor(level) {
this.mobLvl = level;
}
display(elementClass) {
var ele = new CElement(this.mobLvl + "<br>", 'p', "color:red;");
ele.display(elementClass);
}
}
var mob = new Mob(1, "div", "color:red;");
mob.display(".someClassName");
<div class="someClassName"></div>
Looks like you forgot the this. in your CElement.display function. This makes it so that the element variable is undefined, and thus it doesn't append anything to the document.

Remove event listener doesn't work as it should

I simply tried to addEventListener and removeEventListener to element, but it doesn't remove.
I suppose that the problem could be with parameters, but I used them to follow the DRY. So I could simply reuse it like nextSection.addEventListener('mouseover', showContent(event, nextSection)) and so on and so on so I do not need any if statements or stuff like that.
* EDIT *
I made some more examples of elements that I will be using. There’s a chance, that there will be event more. If I do not use parameter, there would be a lot more of functions. Also, there will be click instead of mouse events on mobile, so I need to remove them.
As I understand now, the problem is with return statement. If I use event instead of parameter and so event.target I get some weird bug.
const loginSection = document.querySelector('#js-login-section');
const searchSection = document.querySelector('#js-search-section');
const shoppingBagSection = document.querySelector('#js-shopping-bag-section');
const wishlistSection = document.querySelector('#js-wishlist-section');
function showContent(element) {
return () => {
const toggle = element.lastElementChild;
toggle.style.maxHeight = toggle.scrollHeight + 'px';
}
}
function hideContent(element) {
return () => {
const toggle = element.lastElementChild;
toggle.style.maxHeight = null;
}
}
/* Media queries - min width 992px */
loginSection.addEventListener('mouseover', showContent(loginSection));
loginSection.addEventListener('mouseout', hideContent(loginSection));
searchSection.addEventListener('mouseover', showContent(searchSection));
searchSection.addEventListener('mouseout', hideContent(searchSection));
shoppingBagSection.addEventListener('mouseover', showContent(shoppingBagSection));
shoppingBagSection.addEventListener('mouseout', hideContent(shoppingBagSection));
wishlistSection.addEventListener('mouseover', showContent(wishlistSection));
wishlistSection.addEventListener('mouseout', hideContent(wishlistSection));
/* Media queries - max width 992px */
loginSection.removeEventListener('mouseover', showContent(loginSection));
loginSection.removeEventListener('mouseout', hideContent(loginSection));
searchSection.removeEventListener('mouseover', showContent(searchSection));
searchSection.removeEventListener('mouseout', hideContent(searchSection));
shoppingBagSection.removeEventListener('mouseover', showContent(shoppingBagSection));
shoppingBagSection.removeEventListener('mouseout', hideContent(shoppingBagSection));
wishlistSection.removeEventListener('mouseover', showContent(wishlistSection));
wishlistSection.removeEventListener('mouseout', hideContent(wishlistSection));
Thank you in advance!
What is happening is that return () => {}; is returning a new function every time it's run. So every time you call one of your functions a new event handler is being created.
This means that the handler that is added is different to the one you're trying to remove.
To remedy this, I'd keep it simple:
const loginSection = document.querySelector('#js-login-section');
function showContent(e)
{
const toggle = e.currentTarget.lastElementChild;
toggle.style.maxHeight = toggle.scrollHeight + 'px';
}
function hideContent(e)
{
const toggle = e.currentTarget.lastElementChild;
toggle.style.maxHeight = null;
}
loginSection.addEventListener('mouseover', showContent);
loginSection.addEventListener('mouseout', hideContent);
loginSection.removeEventListener('mouseover', showContent);
loginSection.removeEventListener('mouseout', hideContent);
I'm not sure what you want to avoid repeating, so I can't advise on that, but I'm sure you'll figure it out.
const loginSection = document.querySelector('#js-login-section');
function showContent(event) {
var element = event.target;
return () => {
const toggle = element.lastElementChild;
toggle.style.maxHeight = toggle.scrollHeight + 'px';
}
}
function hideContent(event) {
var element = event.target;
return () => {
const toggle = element.lastElementChild;
toggle.style.maxHeight = null;
}
}
loginSection.addEventListener('mouseover', showContent);
loginSection.addEventListener('mouseout', hideContent);
loginSection.removeEventListener('mouseover', showContent);
loginSection.removeEventListener('mouseout', hideContent);
You must set in events method function without call. Element you can get from event event.target
In your code, I found the following errors,
param 'event' will be always undefined - the event should go as a parameter to inner function.
you don't need closure here - You can directly assign the function without creating an inner function and access the element with event.target or this
with your implementation, you should pass the same handler reference used in addEventListener to removeEventListener. So, you should store the handler in a variable and pass it to both addEventListener and removeEventListener
Solution:
if you don't know the handler name, you can use window.getEventListeners to do the magic,
window.getEventListeners returns a dictionary of events associated with the element.
function removeEventListener(el, eventName) {
if (!el) {
throw new Error('Invalid DOM reference passed');
}
const listeners = getEventListeners(el)[eventName] || [];
listeners.forEach(({
listener
}) => {
removeEventListener(eventName, listener);
});
}
function removeAllEventListener(el) {
if (!el) {
throw new Error('Invalid DOM reference passed');
}
const events = Object.entries(getEventListeners(el) || {});
events.forEach(([eventName, listeners]) => {
listeners.forEach(({
listener
}) => {
removeEventListener(eventName, listener);
});
});
}
// example
// remove mouseout event
removeEventListener(loginSection, 'mouseout');
// remove all event listeners
removeAllEventListener(loginSection);

Trouble with functions and local storage

This code originally took input.value and added it to the page. I added local storage to this project and the code is already written but I’m having a hard time displaying the input to the page from localStorage. The input is stored in local storage as objects in an array. I wrote a for loop to loop through those values and pass them to functions that builds the element and appends it to the li and later appends to the ul. It’s not displaying on the page and I’m not getting any errors in the console. I'm not sure where to turn so heres my code:
function fetchInvite() {
const rsvpInvite = JSON.parse(localStorage.getItem("Invitees"));
const rsvpList = document.getElementById('invitedList');
for(var i = 0; i < rsvpInvite.length; i++) {
const name = rsvpInvite[i].name;
const confirm = rsvpInvite[i].confirmed;
createLI(name, confirm);
function createLI(name, confirm) {
const li = document.createElement('li');
function createElement(elementName, property, value) {
const element = document.createElement(elementName);
element[property] = value;
return element;
}
function appendToLI (elementName, property, value) {
const element = createElement(elementName, property, value);
li.appendChild(element);
return element;
}
appendToLI('span', 'textContent', name);
appendToLI('label', 'textContent', confirm)
.appendChild(createElement('input', 'type', 'checkbox'));
appendToLI('button', 'textContent', 'edit');
appendToLI('button', 'textContent', 'remove');
return li;
}
}
}
The full project is available here: https://github.com/tianniNakiMyers/RSVP_TeamTreeHouse/blob/master/app.js
The problem with your code is that you probably never called fetchInvite.
Apart from that, here is a refactoring of your code:
function elt(parent, tag, text) { // create element of tagname tag and append it to a parent (if provided) then set its textContent to text and return it
var el = document.createElement(tag);
if(parent) {
parent.appendChild(el);
}
el.textContent = text;
return el;
}
function fetchInvite() {
const rsvpInvite = JSON.parse(localStorage.getItem("Invitees"));
const rsvpList = document.getElementById('invitedList');
for(var i = 0; i < rsvpInvite.length; i++) {
const name = rsvpInvite[i].name;
const confirm = rsvpInvite[i].confirmed;
const li = elt(rsvpList, 'li', ''); // create an li element with empty text and append it to rsvpList
elt(li, 'span', name); // create a span whose text is name and append it to li
elt(elt(li, 'label', confirm), 'input', '').type = 'checkbox'; // create an input append it to a label element that contain the text confirm and that is appended to li, then set type of input to 'checkbox'
elt(li, 'button', 'edit'); // ...
elt(li, 'button', 'remove');
}
}
fetchInvite(); // don't forget to call it

JavaScript equivalent to angularjs ng-if

If I want to remove/add element on DOM I just use ng-if and the code under it does not compile into to DOM, can I do the same using pure js? I don't want the HTML code inside my js code.
Hiding it using CSS:
<div id = "infoPage" style="display: none;">
Will still insert the element to the DOM.
EDIT
The condition for showing or not is based on a flag like:
var show = false; //or true
You can try something like this:
Idea:
Create an object that holds reference of currentElement and its parent (so you know where to add).
Create a clone of current element as you want to add same element after its removed.
Create a property using Object.defineProperty. This way you can have your own setter and you can observe changes over it.
To toggle element, check
If value is true, you have to add element. But check if same element is already available or not to avoid duplication.
If false, remove element.
var CustomNGIf = function(element, callback, propertyName) {
var _value = null;
// Create copies of elements do that you can store/use it in future
this.parent = element.parentNode;
this.element = element;
this.clone = null;
// Create a property that is supposed to be watched
Object.defineProperty(this, propertyName, {
get: function() {
return _value;
},
set: function(value) {
// If same value is passed, do nothing.
if (_value === value) return;
_value = !!value;
this.handleChange(_value);
}
});
this.handleChange = function(value) {
this.clone = this.element.cloneNode(true);
if (_value) {
var index = Array.from(this.parent.children).indexOf(this.element);
// Check if element is already existing or not.
// This can happen if some code breaks before deleting node.
if (index >= 0) return;
this.element = this.clone.cloneNode(true);
this.parent.appendChild(this.element);
} else {
this.element.remove();
}
// For any special handling
callback && callback();
}
}
var div = document.getElementById('infoPage');
const propName = 'value';
var obj = new CustomNGIf(div, function() {
console.log("change")
}, propName);
var count = 0;
var interval = setInterval(function() {
obj[propName] = count++ % 2;
if (count >= 10) {
window.clearInterval(interval);
}
}, 2000)
<div class='content'>
<div id="infoPage"> test </div>
</div>

I try to write the java script code like oops how to extend the DOM elements?

In my code like ,
function ElementBase(name) {
this.tagName = typeof name != "" ? name : 'div';
this.createElem();
}
ElementBase.prototype = {
createElem: function() {
this.elem = document.createElement(this.tagName);
},
getIndex: function() {
var nodes = this.elem.parentNode.childNodes,
node;
var i = count = 0;
while ((node = nodes.item(i++)) && node != this.elem)
if (node.nodeType == 1) count++;
return (count);
}
};
I try to create the DOM element tag is "div".
function Div() {
this.tagName = 'div'
ElementBase.call(this, this.tagName);
}
Div.prototype = Object.create(ElementBase.prototype);
My Question is,
1) How to access the getIndex function from the html document after inserting the created objects?
example:
var div = new Div();
div.id = "d1"
document.body.appendChild(div.elem);
// After div.getIndex() working
Then some situation i need the index value of that div (id="d1") element from document.
var d= document.getElementById("d1");
d.getIndex() //not working
What mistakes i did it in above code?
thanks advance..
I think when you do document.body.appendChild(div.elem) you just do document.body.appendChild(document.createElement('div')) nothing more.
And when you do var d= document.getElementById("d1"); d is just an object return from the DOM that has nothing to do with your var div
what you can do is:
Div.prototype.getIndex.call(d);
But that doesn't actually extend your object. Actually extending a DOM object is a bad practice (check this http://perfectionkills.com/whats-wrong-with-extending-the-dom/).
Look closely at your code.
div is an instance of Div and it has a property .elem that holds the actual DOM element.
So when you do div.id = "d1", you are not setting the id of the DOM element.
var div = new Div();
div.id = 'd1'; // <div></div>
div.elem.id = 'd1'; // <div id="d1"></div>
But there's one more problem: when you do d= document.getElementById("d1"), what you get is a DOM element, not an instance of Div().
Since .getIndex() is defined on .prototype of Div(), plain old DOM elements don't have access to it.
How you solve this situation depends on what exactly you need to accomplish with your code.
Edit 1: In response to OP's comment:
document.getElementById() returns an instance of HTMLDivElement, which is fundamentally different from an instance of Div.
One solution is to use a setter method:
function Div() {
// ...
}
Div.prototype.setId = function setId(id) {
this.elem.id = id;
}
var div = new Div();
div.setId('d1'); // same as doing div.elem.id = 'd1';
another solution is to use id in the constructor function itself:
function Div(id) {
// ...
this.elem.id = id; // or you can use "this.setId(id)"
/*
if "id" is provided,
it will take that value,
else it is set to "undefined",
which is the same as not being set
*/
}
Div.prototype.setId = function setId(id) {
this.elem.id = id;
}
var div = new Div('d1'); // same as doing div.elem.id = 'd1';
div.setId('d2'); // same as doing div.elem.id = 'd2';

Categories