I am having some issues figuring out why my created Element won't append to the DOM within my function.
let startText = document.querySelector('.text-container').innerHTML = "Lets Start.";
let lvlTitle = document.querySelector('.text-container');
let background = document.querySelector('.full-page');
lvlTitle.addEventListener('click', countChangeBackground);
var clicks = 0;
function countChangeBackground() {
clicks += 1;
var message = "";
if (clicks == 1) {
message = "Test";
background.style.backgroundColor = "#f81884";
lvlTitle.style.color = "#f2eee2";
} else if (clicks == 2) {
message = "Test2";
background.style.backgroundColor = "#f5ce28";
lvlTitle.style.color = "black";
} else if (clicks == 3) {
message = "Add Li to ul";
var ul = document.querySelector('.text-container');
var li = document.createElement('li');
li.className = 'text-content';
li.appendChild(document.createTextNode('New Text'));
ul.appendChild(li);
console.log(li);
} else {
message = startText;
background.style.backgroundColor = "#f2eee2";
lvlTitle.style.color = "black";
}
lvlTitle.innerHTML = message;
};
<div class="full-page">
<div class="click-container">
<ul class="text-container">
</ul>
</div>
</div>
And here is a jsfiddle:
Append Li to DOM
This is the line causing you problems:
lvlTitle.innerHTML = message;
When you set the innerHTML, if there are other nodes inside of that element, they are overwritten.
You need to alter your logic to only set the innerHTML in some cases.
I've updated your jsfiddle here: http://jsfiddle.net/jepoqd01/
You are overwriting the entire container with innerHTML and not reaching the correct content text
See solution add new variable lvlContainer with get the .text-container and lvlText get the .text-content
let startText = document.querySelector('.text-content').innerHTML = "Lets Start.";
let lvlTitle = document.querySelector('.text-content');
let lvlContainer = document.querySelector('.text-container');
let background = document.querySelector('.full-page');
lvlTitle.addEventListener('click', countChangeBackground);
var clicks = 0;
function countChangeBackground() {
clicks += 1;
var message = "";
if (clicks == 1) {
message = "Test";
background.style.backgroundColor = "#f81884";
lvlContainer.style.color = "#f2eee2";
} else if (clicks == 2) {
message = "Test2";
background.style.backgroundColor = "#f5ce28";
lvlContainer.style.color = "black";
} else if (clicks == 3) {
message = "Add Li to ul";
var ul = document.querySelector('.text-container');
var li = document.createElement('li');
li.className = 'text-content';
li.appendChild(document.createTextNode('New Text'));
ul.appendChild(li);
console.log(li);
} else {
message = startText;
background.style.backgroundColor = "#f2eee2";
lvlContainer.style.color = "black";
}
lvlTitle.innerHTML = message;
};
is in jsfiddle: http://jsfiddle.net/lucaslimax/7jx194gp/37
Related
Hello I want to implement a carousel in Java Script.
When I launch the page the image loads very strangely and the bootstrap carousel does not work. Do I need to import something to make it work? If I make it static in HTML it works. However the task is to load the images with iiif. Also, there would be way too many images to make it static. Also, I would like to have a filter function implemented which only works dynamically.
Can someone help me how to implement the carousel?
Here is all the JS code:
'import bootstrap.js'
function changeIIIFInformation(iiif, size, quality) {
var index = iiif.indexOf('full')
iiif = iiif.substring(0, index)
iiif += "full/"
iiif += "pct:" +size.toString() +"/"
iiif += "0/"
iiif += quality +".jpg"
return iiif
}
function search_period(period, max_num, offset) {
var count = 0;
var link = ""
var div = document.createElement('div')
var div1 = document.createElement ('div')
var a=document.createElement('a')
var span1=document.createElement('span')
var span2=document.createElement('span')
a.setAttribute('class','carousel-control-prev')
a.setAttribute('href','#carouselExampleControls')
a.setAttribute('role','button')
a.setAttribute('data-slide','prev')
span1.setAttribute('class','carousel-control-prev-icon')
span1.setAttribute('aria-hidden', 'true')
span2.setAttribute('class','sr-only')
span2.innerHTML = 'Previous'
div.setAttribute('class', 'carousel slide')
div.setAttribute('data-ride', 'carousel')
div.setAttribute('id', 'carouselExampleControls')
div1.setAttribute('class','carousel-inner')
var h2 = document.createElement('h2')
var iiif = ""
h2.innerHTML = period
document.body.appendChild(h2)
const keys = Object.keys(urls);
for(const elem of keys) {
var label = urls[elem].label
for(const el of urls[elem].variants) {
if(el.label.includes('front')) {
iiif = el.url
}
}
if(!periods.hasOwnProperty(label)) {
continue;
}
if(periods[label] != period) {
continue;
}
if(count < offset) {
count+=1
continue
}
var div2= document.createElement ('div')
div2.setAttribute('class','carousel-item active')
link = changeIIIFInformation(iiif, 10, "default")
var figure = document.createElement('figure')
var figcaption = document.createElement('figcaption')
var linkToImage = document.createElement('a')
//linkToImage.setAttribute('#image', label)
linkToImage.setAttribute('href', 'annotator#'+label)
linkToImage.innerHTML = label
figcaption.appendChild(linkToImage)
var image = document.createElement('img')
image.setAttribute("id", "myimg");
image.setAttribute('src', link)
image.setAttribute('class','d-block w-100')
// figure.appendChild(image)
// figure.appendChild(figcaption)
div.appendChild(div1)
div.appendChild(a)
div1.appendChild(div2)
a.appendChild(span1)
a.appendChild(span2)
div2.appendChild(image)
figure.setAttribute("id", "myFigure");
count += 1;
if(count >= max_num+offset) {
break;
}
}
document.body.appendChild(div)
}
function clear_dom() {
var elements = document.getElementsByTagName('div')
var headings = document.getElementsByTagName('h2')
var buttons = document.getElementsByTagName('button')
while(buttons.length > 0) {
buttons.item(0).remove()
}
while(elements.length > 0) {
elements.item(0).remove()
}
while(headings.length > 0) {
headings.item(0).remove()
}
}
function show_initial_load() {
search_period('Ur III (ca. 2100-2000 BC)', )
}
function show_filtered(selected, offset) {
clear_dom()
search_period(selected, 10, offset)
var previous = document.createElement('button')
if(offset-10 < 0) {
previous.onclick = () => show_filtered(selected, 0)
} else {
previous.onclick = () => show_filtered(selected, offset-10)
}
previous.innerHTML = 'Previous'
document.body.appendChild(previous)
var next = document.createElement('button')
next.onclick = () => show_filtered(selected, offset+10)
next.innerHTML = 'More'
document.body.appendChild(next)
}
function update_view() {
var selected = document.getElementById('perriod-select').value
clear_dom()
if(selected == '') {
show_initial_load()
} else {
show_filtered(selected, 0)
}
}
this is the function I have written which will be executed when you click the button('create'). but somehow it is not creating the element.
It gets the value of the text written in the input tag using value attribute.
function create() {
let text1 = document.getElementById('input').value;
if (text1.trim() == "" || text1.trim() == null){
return false;
}
else{
document.getElementById("input").value = "";
}
let li = document.createElement('li');
li.setAttribute('class', 'todo');
document.getElementById('list').appendChild(li);
let p1 = document.createElement('p');
p1.setAttribute('class', 'text');
let teks = document.createTextNode(text1);
p1.appendChild(teks);
li.appendChild(p1);
let btn1 = document.createElement('button');
btn1.innerHTML = 'Edit';
btn1.setAttribute('class', 'buttons1');
btn1.setAttribute('onclick', 'edit()')
let btn2 = document.createElement('button');
btn2.innerHTML = 'Delete';
btn2.setAttribute('class', 'buttons2');
btn1.setAttribute('onclick', 'delete()')
li.appendChild(btn1);
li.appendChild(btn2);
}
I built a list using javascript and add an eventlistener to each li element like this
for (var i = (page - 1) * records_per_page; i < (page * records_per_page); i++) {
var li = document.createElement('li');
li.id= "lijst";
li.className="lijst";
var p = document.createElement('p');
p.className = "name"
p.id = "Naam"
p.innerHTML = obj.Name[i];
li.appendChild(p);
var p = document.createElement('p');
p.className = "adres";
p.id = "Adres"
p.innerHTML = obj.Adres[i];
li.appendChild(p);
var p = document.createElement('p');
p.className = "gsm";
p.id = "GSM"
p.innerHTML = obj.GSM[i];
li.appendChild(p);
myUL.appendChild(li);
}
const element = document.querySelectorAll(".lijst");
element.forEach(function(el){
el.addEventListener('click',function(){
fillDiv(el);
})
});
When I call my function it doesn't recognize the argument I pass to the function.
function fillDiv(el){
FicheNaam = document.getElementById("FicheNaam");
FicheGSM = document.getElementById("FicheGSM");
FicheAdres = document.getElementById("FicheAdres");
FicheNaam.innerHTML = el.querySelector('.naam').textContent;
FicheGSM.innerHTML = el.querySelector('.gsm').textContent;
FicheAdres.innerHTML = el.querySelector('.adres').textContent;
}
function fillDiv(el){
FicheNaam = document.getElementById("FicheNaam");
FicheGSM = document.getElementById("FicheGSM");
FicheAdres = document.getElementById("FicheAdres");
FicheNaam.innerHTML = el.querySelector('.naam').textContent;
FicheGSM.innerHTML = el.querySelector('.gsm').textContent;
FicheAdres.innerHTML = el.querySelector('.adres').textContent;
}
var current_page = 1;
var records_per_page = 2;
var obj = {
Name: ["John","Peter","Ben","Jonathan"],
GSM: ["123","456","789","444"],
Adres: ["Adress1","Adress2","Adress3","Adress4"],
};
function changePage(page){
var btn_next = document.getElementById("btn_next");
var btn_prev = document.getElementById("btn_prev");
var listing_table = document.getElementById("myUL");
var page_span = document.getElementById("page");
if (page < 1) page = 1;
if (page > numPages(obj)) page = numPages(obj);
listing_table.innerHTML = "";
for (var i = (page - 1) * records_per_page; i < (page * records_per_page); i++) {
var li = document.createElement('li');
li.id= "lijst";
li.className="lijst";
var p = document.createElement('p');
p.className = "name"
p.id = "Naam"
p.innerHTML = obj.Name[i];
li.appendChild(p);
var p = document.createElement('p');
p.className = "adres";
p.id = "Adres"
p.innerHTML = obj.Adres[i];
li.appendChild(p);
var p = document.createElement('p');
p.className = "gsm";
p.id = "GSM"
p.innerHTML = obj.GSM[i];
li.appendChild(p);
myUL.appendChild(li);
}
const element = document.querySelectorAll(".lijst");
element.forEach(function(el){
el.addEventListener('click',function(){
fillDiv(el);
})
});
page_span.innerHTML = page +"/"+numPages(obj);
}
function prevPage(){
if (current_page > 1) {
current_page--;
changePage(current_page);
}
}
function nextPage()
{
if (current_page < numPages(obj)) {
current_page++;
changePage(current_page);
}
}
function numPages(obj)
{
return Math.ceil(obj.Name.length / records_per_page);
}
window.onload = function() {
changePage(1);
};
#filldiv{
background-color:grey;
float:right;
}
<div id="filldiv">
<p id="FicheNaam">Name</p>
<p id="FicheGSM">GSM</p>
<p id="FicheAdres">Address</p>
</div>
<a onclick="prevPage()" class="previous" id="btn_prev" aria-label="Previous">
<span aria-hidden="true">«</span></a>
<a class="next" onclick="nextPage()" id="btn_next" aria-label="Next">
<span id="page"></span>
<span aria-hidden="true">»</span> </a>
<ul id="myUL">
</ul>
Included a jsfiddle https://jsfiddle.net/h3o2Lbe8/ and snippet of my code.
I need to fill the div on the right with the information within each li.
Heres the fix:
function fillDiv(el) {
FicheNaam = document.getElementById("FicheNaam");
FicheGSM = document.getElementById("FicheGSM");
FicheAdres = document.getElementById("FicheAdres");
FicheNaam.innerHTML = el.querySelector('.name').textContent;
FicheGSM.innerHTML = el.querySelector('.gsm').textContent;
FicheAdres.innerHTML = el.querySelector('.adres').textContent;
}
Explanation:
The code is fine except for a naming issue.
In the function changePage() you used the classname 'name'
And in the fillDiv function you tried to use classname 'naam' in the query selector
FicheNaam.innerHTML = el.querySelector('.naam').textContent;
An exception occured in the funciton because el.querySelector('.naam') returns undefined and you tried to access .textContent property.
Exception in console
Screenshot of your code working
I am confused at to why my function executes before the start button is pressed. I looked around and they said the onclick will run at the start if you don't but the code to be executed when the button is clicked in a function. But mine is a function... This code is supposed to create 4 buttons when the start button is pressed. But right now the 4 buttons appear right away.
EDIT: Here is the full code.
var log = document.getElementById("Log");
log.addEventListener("click", login); // Runs the Login Function
var email;
var password;
// Makes an alert to test input values.
function login() {
form = document.getElementById("form");
var text = "E-Mail: " + form.elements[0].value + " Password: " + form.elements[1].value;
alert (text);
}
// Testing Function
function helloWorld() {
alert ("Hello World");
}
//create the snake
function createSnake() {
var bodyLength = 5; //snake length
var body = []; //snake body
var head = [10, 10]; //snake head starting position;
// create the variables to edit for the body positions loop
var row = head[0];
var col = head[1];
// set the snake body positions
for (var i=0;i<bodyLength; i++) {
body[body.length] = [row, col];
var cord = row + "_" + col;
// Set the head Green
if (i == 0) { document.getElementById(cord).style.backgroundColor = 'green';
}
// Set the Body blue
else {document.getElementById(cord).style.backgroundColor = 'blue';}
row++;
}
}
var snakeBool = false; //Bool to test if the snake game has been pressed.
// Create a table function. Creates a gray table for Snake.
function createTable() {
if (!snakeBool) {
// create a table of data
//target the activity div
var activity = document.getElementById("activity");
//create table
var tbl = document.createElement("table");
//table styles
tbl.style.borderCollapse = "collapse";
tbl.style.marginLeft = '12.5px';
//create size var
//var size = '5px';
//set the row and column numbers
var tr_num = 30;
var td_num = 25;
//start the loops for creating rows and columns
for (var i = 0; i < tr_num; i++) {
var tr = document.createElement("tr"); // create row
//tr style
tr.style.height = '7px';
for (var j = 0; j < td_num; j++) { //start loop for creating the td
var td = document.createElement("td"); //create td
td.style.width = '5px';
if (i == 0 || i == (tr_num-1) || j == 0 || j == (td_num-1)) {
td.style.backgroundColor = "white";
}
else {
td.style.backgroundColor = "gray";
}
td.id = i + "_" + j; //set id to td
//td.appendChild("data"); //append data to td
tr.appendChild(td); //append td to row
}
tbl.appendChild(tr); //append tr to the table
}
activity.appendChild(tbl); //append the table to activity div
createSnake(); //Creates the snake body.
snakeBool = true; //Sets the Snake Bool to true since game has been created.
//create Start button
var b1 = document.createElement("input");
b1.type = "button";
b1.value = "Start";
b1.onClick = startGame;
activity.appendChild(b1);
} // end of if Function
}
function startGame() {
createButtons();
}
function createButtons() {
var b1 = document.createElement("input");
b1.type = "button";
b1.value = "Up";
//b1.onClick = func
activity.appendChild(b1);
var b2 = document.createElement("input");
b2.type = "button";
b2.value = "Down";
//b1.onClick = func
activity.appendChild(b2);
var b3 = document.createElement("input");
b3.type = "button";
b3.value = "Left";
//b1.onClick = func
activity.appendChild(b3);
var b4 = document.createElement("input");
b4.type = "button";
b4.value = "Right";
//b1.onClick = func
activity.appendChild(b4);
}
// when button is pressed, do createTable function
document.getElementById("gamesButton").addEventListener("click", createTable);
Using the brackets, you’re immediately invoking the startGame function. Its return value is then assigned to the onClick property.
You most likely want to assign the function itself, so it’s executed when the onClick event fires. To do so, change this
b1.onClick = startGame();
to this
b1.onClick = startGame;
So I'm trying to to basically dynamically create li's inside an array, and I would like to create a 'delete' button within each li, so that when I click that li, I can delete that specific li.
I know this seems very basic, but I've been looking at JS for hours now, and am starting to really confuse myself here.
I keep getting errors like addChild() is not a function... I feel like I'm close, but no cigar. Thanks in advance!
Anyway, here's my add function:
function add(){
var deleteBtn = document.createElement('input');
deleteBtn.type = 'submit';
deleteBtn.name = 'addButton';
deleteBtn.className = 'deleteButton';
for(i=0;i<1;i++){
id_number[i] = i+1;
var newSong = '<li class="li_test" id="' + id_number[i] + '">' + "<span>" + "</span>" + '</li>';
// $(newSong).appendChild(deleteBtn);
$(deleteBtn).appendTo("#playlist-1");
$(newSong).appendTo("#playlist-1");
showList.push(newSong);
deleteBtn.addEventListener('click', function(evt) {
deleteFromPlaylist(newSong);
});
}
}
Here's my delete function
function deleteFromPlaylist(newSong){
var deleteBtn = document.getElementsByTagName('deleteButton');
// var deleteMe = deleteBtn.parentNode;
alert(deleteBtn);
for(i=0;i<showList.length;i++){
if(newSong === showList[i]){
showList.splice(i,1);
// var pp = p.parentNode;
// pp.removeChild (p);
deleteMe = deleteMe.parentNode.remove("li_test");
deleteMe.removeChild(deleteBtn);
}
// console.log(deleteMe);
}
}
EDIT: 1 More Related Question
I would like to only add an item if it doesn't exist already in the array. Here is what I have so far. Any tips on where I'm going wrong?
for (i = 0; i < showList.length; i++) {
if (newSong !== showList[i]){
ul_list.innerHTML = newSong;
container_div.appendChild(ul_list); //append the info
container_div.appendChild(deleteBtn);
document.getElementById('playlist-1').appendChild(container_div); //finally add it to the playlist div
showList.push(newSong);
deleteBtn.addEventListener('click', function(evt) {
deleteFromPlaylist(evt, newSong);
});
inc++;
alert("It IS in the Array!");
}else{
alert("This already exists!");
}
}
You seem to have a strange mix of code. Forget the jQuery stuff until you know javascript.
> function add(){
> var deleteBtn = document.createElement('input');
> deleteBtn.type = 'submit';
I don't think that's a good idea. Much better to use type button or a button element.
> deleteBtn.name = 'addButton';
> deleteBtn.className = 'deleteButton';
>
> for(i=0;i<1;i++){
Presumably i will go a bit higher in future. ;-)
> id_number[i] = i+1;
Where did id_number come from?
>
> var newSong = '<li class="li_test" id="' + id_number[i] + '">' + "<span>" + "</span>" + '</li>';
> // $(newSong).appendChild(deleteBtn);
Stick to one method of creating elements. Consider using a document fragment to hold the parts.
> $(deleteBtn).appendTo("#playlist-1");
> $(newSong).appendTo("#playlist-1");
> showList.push(newSong);
Where did showList come from?
> deleteBtn.addEventListener('click', function(evt) {
> deleteFromPlaylist(newSong);
> });
Not all browsers support addEventListener. Since you are only adding one listener, consider just assigning to the button's onclick property. Note that newSong is just a string.
> }
> }
In the other function:
> function deleteFromPlaylist(newSong){
> var deleteBtn = document.getElementsByTagName('deleteButton');
There is no HTML "deleteButton" element, so that will return an empty collection.
> // var deleteMe = deleteBtn.parentNode;
> alert(deleteBtn);
> for(i=0;i<showList.length;i++){
> if(newSong === showList[i]){
> showList.splice(i,1);
> // var pp = p.parentNode;
>
> // pp.removeChild (p);
> deleteMe = deleteMe.parentNode.remove("li_test");
Where did deleteMe come from? You commented out where it was declared and it hasn't been assigned a value, so deleteMe.parentNode will throw an error.
> deleteMe.removeChild(deleteBtn);
> }
> // console.log(deleteMe);
> }
> }
> }
Anyhow, here's some working code, it's still pretty awful but I'll leave it to you go improve it.
<script>
var showList = [];
function add(){
var id_number = [];
var deleteBtn = document.createElement('input');
deleteBtn.type = 'button';
deleteBtn.name = 'addButton';
deleteBtn.className = 'deleteButton';
deleteBtn.value = 'Delete Button';
for (i=0; i<1; i++) {
id_number[i] = i + 1;
// '<li class="li_test" id="' + id_number[i] + '">' + "<span>" + "</span>" + '</li>';
var newSong = document.createElement('li');
newSong.className = 'li_test';
newSong.id = id_number[i];
newSong.appendChild(document.createElement('span').appendChild(document.createTextNode('song')));
showList.push(newSong);
deleteBtn.onclick = (function(id) {
return function(){deleteFromPlaylist(id);}
}(newSong.id));
newSong.appendChild(deleteBtn);
document.getElementById('playlist-1').appendChild(newSong);
}
}
function deleteFromPlaylist(id) {
var song = document.getElementById(id);
if (song) {
song.parentNode.removeChild(song);
}
}
window.onload = function() {
add();
}
</script>
<ul id="playlist-1">
<li>Original
</ul>
I've altered your code and functions to purely use javascript, instead of a mixture containg jquery. I've added comments in the code to explain my actions. If you have any questions, feel free to ask.
var showList = [];
var inc = 1;
function add() {
//create the container element. If we do this, keeping track of all elements
//becomes easier, since we just have to remove the container.
var container_div = document.createElement('div');
container_div.id = "cont_" + inc;
var ul_list = document.createElement('ul');
var deleteBtn = document.createElement('input');
deleteBtn.type = 'button';
deleteBtn.value = 'remove song';
deleteBtn.name = 'addButton';
deleteBtn.className = 'deleteButton';
var id_number = [];
var newSong = "";
for (i = 0; i < 1; i++) {
id_number[i] = i + 1;
newSong += '<li class="li_test" id="cont_' + inc + '_song_id_' + id_number[i] + '">' + "<span>test " + inc + "</span>" + '</li>\n'; //all ids must be unique, so we construct it here
}
ul_list.innerHTML = newSong;
container_div.appendChild(ul_list); //append the info
container_div.appendChild(deleteBtn);
document.getElementById('playlist-1').appendChild(container_div); //finally add it to the playlist div
showList.push(newSong);
deleteBtn.addEventListener('click', function(evt) {
deleteFromPlaylist(evt, newSong);
});
inc++;
}
function deleteFromPlaylist(evt, newSong) {
var deleteBtn = evt.target; //target the button clicked, instead of a list of all buttons
var container_div = deleteBtn.parentNode; //get the parent div of the button
var cont_parent = container_div.parentNode; //and the parent of the container div
for (i = 0; i < showList.length; i++) {
if (newSong === showList[i]) {
showList.splice(i, 1);
}
}
cont_parent.removeChild(container_div); //finally, remove the container from the parent
}
Update:
I've modified the above function to strictly use objects, rather than strings, because it is easier to extract relevant information from objects, than strings.
I've added in comments to assist with understanding the code. Again, if you have any questions, feel free to ask.
function add() {
var list_bool;
//create the container element. If we do this, keeping track of all elements
//becomes easier, since we just have to remove the container.
var container_div = document.createElement('div');
container_div.id = "cont_" + inc;
var ul_list = document.createElement('ul');
var deleteBtn = document.createElement('input');
deleteBtn.type = 'button';
deleteBtn.value = 'remove song';
deleteBtn.name = 'addButton';
deleteBtn.className = 'deleteButton';
var list_item = document.createElement("li"); //create list element
list_item.className = "li_test"; //set element class
var list_span = document.createElement("span"); //create span element
list_span.innerHTML = "test"; //set span text
list_item.appendChild(list_span); //append span to list element
ul_list.appendChild(list_item); //append list element to un-ordered list element
var list_bool = false; //create local boolean variable
if (showList.length > 0) { // loop through showList if it isn't empty
for (var i = 0; i < showList.length; i++) {
if (showList[i].innerText !== list_item.innerText) {
list_bool = true; //if song exists(comparing text values, set bool to true
} else if (showList[i].innerText === list_item.innerText) {
list_bool = false; //else, set it to false
break; //break out of loop.. we don't want it becoming true again, now do we?
}
}
} else {
list_bool = true; //showList is empty, set to true
}
if (list_bool) { //if true, do action of appending to list
container_div.appendChild(ul_list); //append the info
container_div.appendChild(deleteBtn);
document.getElementById('playlist-1').appendChild(container_div); //finally add it to the playlist div
showList.push(list_item);
deleteBtn.addEventListener('click', function(evt) {
deleteFromPlaylist(evt, newSong);
});
inc++;
}
}
DEMO, notice that add() is executed twice, but because the song 'test' already exists, it only executes the end action once.