Hi I am just learning Javascript and after following some tutorials I thought it would be nice to practise some Javascript by making stuff.
So now I am trying to make a very easy to-do-list. Just for practise, but I get stuck.
I managed to add items with a remove-button to an UL with JS. But, BUT:
How do I make it so; when you click on the removeMe button, that only that Li will be removed?
What should I use?
Here's my code:
var buttonAdd = document.getElementById('but1');
var buttonRemove = document.getElementById('but2');
var ul = document.getElementById('myUl');
function addLi() {
var newLi = document.createElement('li');
var removeThis = document.createElement('button');
var textInput = document.getElementById('inputText').value;
if(textInput === ""){
alert('Add text');
}else{
newLi.innerHTML = textInput;
newLi.appendChild(removeThis);
removeThis.innerHTML = "Remove me";
removeThis.setAttribute("onClick", "removeMe(this);");
ul.appendChild(newLi);
}
}
buttonAdd.onclick = function() {
addLi();
};
buttonRemove.onclick = function() {
ul.innerHTML = "";
};
function removeMe(item){
//get called when clicked on the remove button
}
and my HTML:
<body>
<ul id="myUl"></ul>
<input id="inputText" type="text"><br />
<button id="but1">Add stuff</button><br />
<button id="but2">Remove all</button>
</body>
Thanks
The function remove() is a brand new DOM 4 method and not very widely supported yet. The clunky, but bulletproof way would be:
function removeMe(item){
item.parentElement.parentElement.removeChild(item.parentElement);
}
or with a bit more elegance:
function removeMe(item){
var parent = item.parentElement;
parent.parentElement.removeChild(parent);
}
http://jsfiddle.net/BtbR4/
Also be careful with this:
removeThis.setAttribute("onClick", "removeMe(this);");
Handing a function reference as a string is always a bad idea for several reasons (eval'ing the string, messing up the scope). There are several better options:
removeThis.onclick = removeMe;
or if you need to hand over parameters
removeThis.onclick = function(){removeMe(your,parameters)};
The best option however is to attach eventhandlers always like this:
Element.addEventListener("type-of-event",functionReference);
You just need to remove the parent node (the li), as I've shown using jsbin.
function removeMe(item){
item.parentNode.remove();
}
Please note Blue Skies's comment that this may not work across all browsers, an alternative is:
var par = item.parentNode; par.parentNode.removeChild(par);
a cleaner way to do things is to add
removeThis.onclick = removeMe;
and
function removeMe(mouseEvent){
this.parentNode.remove();
}
This is consistent with how you add the other onclick functions in your code. Since you said you are learning js, it is a good idea to learn how events and functions work. So, the take away from this is that the 'this' of a function that is attached to an object is the object itself (the removeThis object in this case), and event handlers give you access to the event that invoked them (mouseEvent) in the argument list.
JSfiddle: http://jsfiddle.net/QT4E3/
Related
I posted this question before, among others. But it was suggested I need to ask a more specific or focused question.
I am working on an output history log on a single page. And I want to make it so each output it's self is contained in box object that can be closed or deleted individually. Like this.
Now I have managed to get everything working to the point where it will nicely output to a box with a close button. However the close button it's self will not function in this case.
So, I am trying to output it like this...
HTML:
<p>History log:</p><br><div style="white-space:pre-wrap"><ul
id="outputListItem" class="boxcontainer"></ul></div>
SCRIPT:
document.getElementById("Add").onclick = function(e) {
convertOutput();
}
function convertOutput(){
//this is the part I have been trying to get working
convertOutput.addEventListener('close', function() {
this.parentElement.style.display = 'none';
}
});
var output = document.getElementById("output").value;
var li = document.createElement('li');
li.className = "containedboxes";
var dateTime = todayDateTime();
li.innerHTML = "<time id='time'>" + dateTime +"</time><br /> <br />"+ output
+"<br /><br /><span class='close'>×</span>";
document.getElementById('outputListItem').prepend(li);
}
And the script to close the box:
var closebtns = document.getElementsByClassName("close");
var i;
for (i = 0; i < closebtns.length; i++) {
closebtns[i].addEventListener("click", function() {
this.parentElement.style.display = 'none';
});
}
It was suggested to me on the last question I posed I should use convertOutput() right after addEventListener() loop immediately after it. If this is how you do it, i am still quite new to JavaScript, so not sore how to properly do this. I created a fiddle for this also, but for some reason I can't get the script to run properly in the fiddle, But all the code is there to see.
I am looking to solve this using vanilla JavaScript.
I created an example for you. Hopefully this helps you get going :) A couple things to note, I use a data attribute to store the index for the item in the array, so you can delete it when you click on the list item.
document.addEventListener('DOMContentLoaded', function(){
let nameEl = document.querySelector("#name");
let submitEl = document.querySelector("#submit-name");
let historyEl = document.querySelector(".history-list");
let historyList = [
{ name: 'Mitch'},
{ name: 'Max'},
{ name: 'Mike'},
];
function addToList(arr) {
// Clear up list and then update it
while(historyEl.firstChild) {
historyEl.removeChild(historyEl.firstChild);
}
// Update the list with the historyList
for(let item in historyList) {
let name = historyList[item].name;
let listContent = document.createElement("li");
listContent.textContent = name;
// We will use the index to remove items from the list
listContent.setAttribute('data-value', item);
listContent.addEventListener("click", removeFromList)
historyEl.appendChild(listContent);
}
}
function removeFromList(index) {
// Takes the index of the object, and will later remove it
console.log("Removed Item " + this.dataset.value);
historyList.splice(index, 1);
addToList(historyList);
}
addToList(historyList);
submitEl.addEventListener("click", function(event) {
if(nameEl.value) {
// Add the name to the start of the history list array.
historyList.unshift({ name: nameEl.value})
nameEl.value = '';
// Update the dom with the new array
addToList(historyList);
}
});
});
<label for="name">Type Name</label>
<input type="text" name="name" id="name">
<button id="submit-name">Submit Name</button>
<ul class="history-list"></ul>
Hopefully this gives you a good idea on how to get the task done and let me know if you have any questions :)
Your boxes don't respond to the click event simply because your script crashes before the events even get attached to it.
The following block right at the beginning:
document.getElementById("Add").onclick = function(e) {
convertOutput();
}
tries to add a click listener to the HTML element Add which does not exist. If you either remove the code block or add the appropriate element your boxes will have it's click functionality.
Goal:
I'm trying to select a div inside a dom-repeat element.
The code below contains the div I want to select. id="med[[index]]"
Code:
<div class="card">
<h1>Medicijn overzicht:</h1>
<template is="dom-repeat" items="{{employees}}">
<div class="inner-card" id="[[index]]">
<div><span><b>{{item.first}}</b></span> <span>{{item.last}}</span>mg<br><span>{{item.stuks}}</span> stuks</div>
<div id="med[[index]]" style="display:none"><br><br><br><br><br>Goed verhaal</div>
</div>
</template>
</div>
Problem:
The goal here is to have the div expand when the parent gets clicked, but the querySelect seems to be unable to find the element when it is called from within the click function. When preforming this search at the top of the function with a static name it finds it just fine.
attached:function(){
this.async(function() {
var cards = Polymer.dom(this.root).querySelectorAll(".inner-card");
var test = Polymer.dom(this.root).querySelector("#med0"); //Finds the element
console.log(test); // This is fine
console.log("cards",cards);
$(cards).click(function(evt){
var target= evt.currentTarget;
var tindex = target.id;
var targetDiv = Polymer.dom(this.root).querySelector("#med"+tindex); //Doesn't find it.
console.log("#med"+tindex)
console.log(targetDiv);
$(targetDiv).slideDown("slow");
console.log("trigger"+tindex);
});
});
}
It might just be me misunderstanding how this works, I'm very new to this.
Can anyone tell me what I'm doing wrong here?
Thanks in advance.
Another solution would be to add .bind(this) after end of function. I am using it in old projects, where ES6 can't be used. Using .bind(this) seems more 'cleaner'
In your case:
$(cards).click(function(evt){
var target= evt.currentTarget;
var tindex = target.id;
var targetDiv = Polymer.dom(this.root).querySelector("#med"+tindex); //Doesn't find it.
console.log("#med"+tindex)
console.log(targetDiv);
$(targetDiv).slideDown("slow");
console.log("trigger"+tindex);
}.bind(this));
The reference to this.root within click-handler is different than the one referenced above it. root probably does not exist within the this context of the click-handler—it certainly is not the same as the this.root above, since that this is referring to the global this.
You might want to save this or this.root for use within the click handler:
this.async(function() {
var cards = Polymer.dom(this.root).querySelectorAll(".inner-card");
var test = Polymer.dom(this.root).querySelector("#med0");
var root_root=this.root; // **ADD THIS**
$(cards).click(function(evt){
var target= evt.currentTarget;
var tindex = target.id;
var targetDiv = Polymer.dom(root_root).querySelector("#med"+tindex); // **CHANGE THIS**
$(targetDiv).slideDown("slow");
console.log("trigger"+tindex);
});
});
Alternatively, if you can use ES6's arrow functions, you can fix this easier by simply converting the click handler to an arrow function,
$(cards).click((evt) => { …
This will use the this from the surrounding context of the arrow-function.
This is what I've done so far - but I also need to handle and empty comment, clear the field after submission and post multiple comments. Don't want answers really - just hints as to where I need to look and if I'm completely off base.
function registerClickHandler() {
var commentButton = document.getElementByID('postComment');
commentButton.onclick = addComment();
}
function addComment() {
var list = document.getElementByID('commentList');
var commentContent = document.getElementByID('comment')
var newComment = document.createElement('li');
newComment.appendChild(document.createTextNode(commentContent));
list.appendChild(newComment);
}
<ul id='commentList'>
</ul>
<form>
<input type='text' id='comment'/>
<input type='button' id='postComment' value='Post'/>
</form>
Take a look to this code working example, basically you have syntax error:
change all ocurrencies of getElementByID for getElementById
document.getElementByID('postComment');
for
document.getElementById('postComment');
And call the function which define the click handler
registerClickHandler();
Get the value for the element
var commentContent = document.getElementById('comment').value;
Some hints:
There's no getElementByID function, only getElementById, because javascript is case-sensitive.
commentButton.onclick = addComment(); is wrong because you call addComment function instead of assigning it as an event-handler to the onclick event. You should remove the parentheses commentButton.onclick = addComment;.
Use document.getElementById('comment').value; to get or set the value of your text input.
I have the syntax below. I was wondering if this part: $($(this).parent().siblings('div')[0]) could be writen more elegantly by accessing a jQuery object directly without the need to use $(); again.
I used .parent().siblings and didn't use the class of div because I want to reuse the code somewhere where the classes are different.
$('textarea').click(function(){
$($(this).parent().siblings('div')[0]).html('<span>140</span>');
$($(this).parent().siblings('div')[1]).html('<span>Reply</span>');
});
<div class="post_area2">
<div class="wrap_area2 left">
<textarea></textarea>
</div>
<div class="word_c left"></div>
<div class="submit left"></div>
</div>
You can use .eq() method.
$('textarea').click(function(){
$(this).parent().siblings('div')
.eq(0).html('<span>140</span>').end()
.eq(1).html('<span>Reply</span>');
});
Try
$('textarea').click(function(){
$(this).parent().next().html('<span>140</span>')
.next().html('<span>Reply</span>');
});
http://jsfiddle.net/rVfJ4/
I agree with sweetamylase's answer.
That code should work. Because it is using native javascript, I think it is good for performance.
Or use class name for determinate div:
$('textarea').click(function(){
var parentDiv = $(this).parents('div.post_area2'); // avoid to use $(this) multi time in this function
parentDiv.find('div.word_c').html('<span>140</span>');
parentDiv.find('div.submit').html('<span>Reply</span>');
});
That will work exactly even you add new another div.
Don't use 'eq()' if you have another solution.
I'm sorry, I'm not enough reputation for voting.
I would re-write it as such:
$('textarea').click(function(){
var divContainer = $(this).parent().siblings('div');
divContainer[0].innerHTML = '<span>140</span>';
divContainer[1].innerHTML = '<span>Reply</span>';
});
You can use native attribute innerHTML to set the contents of the DOM element, it is equivalent to using .html() minus the requirement of invoking the jQuery object.
Also, it depends if your coding style likes to do chaining because that's really easily done with jQuery, but can be difficult to read when the line becomes long.
You should give the other divs extra classes to represent what they mean.
$('textarea').click(function(){
$(this).parents('form').children('.hide-until-using-form').show();
});
This creates a highly reusable function that doesn't depend on how many siblings are involved, what the text of the button is (Post? Edit?), etc.
There is infinite ways to do it,
You can think a bit out of the box, i'm not sure that the latest version of jquery optimize the access to elements, if i'm wrong feel free to make me sorry on it.
But here are a few ways to do it:
var parentSelector = $(this).parent().parent();
var parentSelector = $(this).parents('#uniqueId');
var currentId = this.id;
var childrenSelector = parentSelector.children('div[id!="' + currentId + '"'];
var one = $(childrenSelector.get(0));
var two = $(childrenSelector.get(1));
A simple way to optimize the access to elements is to wrap/create new function that save reference to DOM elements, there is some issues that need to be consider but you can make a nice dom-cache library.
Perhaps like this
$('textarea').click(function () {
var parentSiblings = $(this).parent().siblings('div');
$(parentSiblings[0]).html('<span>140</span>');
$(parentSiblings[1]).html('<span>Reply</span>');
});
Or
$('textarea').click(function () {
var parentSiblings = $($(this).parent().siblings('div'));
parentSiblings.eq(0).html('<span>140</span>');
parentSiblings.eq(1).html('<span>Reply</span>');
});
Others may see using vanilla javascript more elegant.
function empty(element) {
"use strict";
while (element.hasChildNodes()) {
element.removeChild(element.lastChild);
}
}
function addEvent(nodeList, type, callBack) {
"use strict";
var length = nodeList.length,
i = 0;
while (i < length) {
nodeList[i].addEventListener(type, callBack, false);
i += 1;
}
}
addEvent(document.getElementsByTagName("textarea"), "click", function (evt) {
"use strict";
var divContainer = evt.target.parentNode.parentNode.getElementsByTagName("div"),
span;
empty(divContainer[1]);
span = document.createElement("span");
span.textContent = "140";
divContainer[1].appendChild(span);
empty(divContainer[2]);
span = document.createElement("span");
span.textContent = "Reply";
divContainer[2].appendChild(span);
});
These and others are available on jsfiddle
I guess it depends on how exactly you define more elegant. Sometimes readability is more important, perhaps not including ant 3rd party libraries and using vanilla javascript.
I have a button which appends some elements. I want a button which duplicates this button.
I found some articles about clone(), but I believe that's jQuery?
I can't seem to find the answer but I thought something like this:
var copybutton = create("input");
copybutton.type = "button";
copybutton.id = "copybutton" + counter;
copybutton.value = "copybutton";
addEvent(copybutton, "click", duplicatefunction);
function duplicatefunction()
{
var duplicatebutton = appendbutton.cloneNode (true);
}
Well at least that would make sense to me a little, but of course it doesn't work. How can I do this?
Given:
<div id="original">
<p>Hello World</p>
</div>
You can use something like:
var e1 = document.getElementById("original"), e2;
e2 = e1.cloneNode(true);
Set the parameter to cloneNode true if you want to recursively copy the node and its children.