0

I am working on form. Now the form it's self works great, no issues with it. But I am trying to implement a history log of previous results. I have been able to get it working in a basic sense, when an entry is output to the div, it will appear at the top, than the second one will appear underneath (appending). This is my first issue, I would like most recent entry to appear at the top with the oldest at the bottom in descending order (prepending).

I just cant quite get it to work.

The HTML:

<p>History log:</p><br><div id="SecondDIVOutput" style="white-space: 
pre-wrap"></div> 

The script:

function convertOutput(){
   var output = document.getElementById("output").value;
       SecondDIVOutput.innerHTML+=   "<ul><li><time id='time'></time><br /> 
<br />"+ output +"<br /><br /><span class='close'>&times;</span></li></ul>";
}

I am sure it'll be something really simple I am missing to make this work, but I have been at a loss trying to get it working.

I have two other questions also. I have been also trying to implement a timestamp.

The time stamp works, BUT, will only appear on the first output, the rest will not have the timestamp

Here's the script for the timestamp:

var MONTH_NAME = ['January', 'Febuary', 'March', 'April', 'May', 'June',
              'July', 'August', 'September', 'October', 'November', 
'December'];
function showTime() {
function twoDigit(n) {
    return ('0' + n).slice(-2);
}
function iso8601(date) {
    return date.getFullYear() +
           '-' + twoDigit(1 + date.getMonth()) +
           '-' + twoDigit(date.getDate()) +
           'T' + twoDigit(date.getHours()) +
           ':' + twoDigit(date.getMinutes());
}
function en_US(date) {
    var h = date.getHours() % 12;
    return MONTH_NAME[date.getMonth()] +
           ' '  + date.getDate() +
           ', ' + date.getFullYear() +
           '<br />' + (h == 0 ? 12 : h) +
           ':'  + twoDigit(date.getMinutes()) +
           ' ' + (date.getHours() < 12 ? 'am' : 'pm');
}

var timeEl = document.getElementById('time');
if (timeEl !== null) {
    var now = new Date();
    timeEl.innerHTML = en_US(now);
    timeDiv.setAttribute('datetime', iso8601(now));
   }
};
setInterval(showTime, 1000);

I would love to make this work as it would really look good, and be useful for the history log.

Lastly, I have really been trying (hoping) to make each output a box than can be removed with a close button.

I have been able to make this work to a degree, the output will appear in a box, close button and all, but the button wont function with the script.

The script for it:

var closebtns = document.getElementsByClassName("close");
var i;

for (i = 0; i < closebtns.length; i++) {
closebtns[i].addEventListener("click", function() {
this.parentElement.style.display = 'none';
});
}

I have made a Fiddle with all related code in it, though I couldn't get all of the scripting to run properly within the fiddle. It does work if saved as a .html and opened though. Sorry I am still new to all this and learning. On this last one, is this even possible? I would be really happy if I can get this working.

Thanks so much, I know this is a lot in terms of questions, but my goal is to have the output prepend on top of the old, with a timestamp for each entry, and lastly to make any single entry able to be removed or deleted. I am trying to solve this using vanilla JavaScript.

UPDATE:

I am trying to use the convertOutput() like has been suggested below, to get the function to work, but I am not sure how ti properly call it.

SCRIPT:

function convertOutput(){

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'>&times;</span>";
   document.getElementById('outputListItem').prepend(li);
}
Mulan
  • 129,518
  • 31
  • 228
  • 259
Ty H
  • 79
  • 7
  • 2
    You can still separate these questions while you ask. so as to get the best available solution for a specific issue. Please Ask these 3 questions individually, so that your question will not be classified as too broad and put on hold – Bosco Jun 29 '19 at 00:53
  • 1
    In your fiddle you do not need the – SScotti Jun 29 '19 at 00:58
  • I guess I could repost three separate questions. I did it this way because they are all related to the same overall issue. – Ty H Jun 29 '19 at 01:03
  • 1
    For #3, you need to bind your click event to the document and add the element selector so it works on dynamically added elements: https://stackoverflow.com/a/12673849/576336 You don't need to bind each one individually in a loop – TommyGunn32 Jun 29 '19 at 01:04
  • I actually tired that, I deleted all script tags, and it went from partially working, to not at all. – Ty H Jun 29 '19 at 01:05
  • @Nidhin Joseph, which one do you want to be accepted? the first, second, third. I think you answers can only be voted up. – Bosco Jun 29 '19 at 08:13
  • @Bosco well, it depends on the OP owner to decide which answer best help someone reading this question. My opinion is the second one. – Nidhin Joseph Jun 29 '19 at 08:17
  • You should **almost never** use `.innerHTML +=`. There are some [really serious drawbacks to doing so](https://stackoverflow.com/a/45257549/3773011). For what you are wanting to do here, [`.insertAdjacentHTML()`](https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentHTML) should be your friend. – Makyen Jul 01 '19 at 22:12

4 Answers4

1

To answer your first question, you can use prepend() to attach contents to the beginning of the element.

function prepend() {
  let div = document.createElement('div');
  div.innerHTML = new Date().getTime();
  document.getElementById('your_id').prepend(div);
}
<div id="your_id"></div>
<button onclick="prepend()">Do Prepend</button>
Nidhin Joseph
  • 9,981
  • 4
  • 26
  • 48
1

To answer your second question with timeclock, you had a typo by using timeDiv.setAtt... as timeDiv is not declared. Please find the corrected code

var MONTH_NAME = ['January', 'Febuary', 'March', 'April', 'May', 'June',
  'July', 'August', 'September', 'October', 'November',
  'December'
];

function showTime() {
  function twoDigit(n) {
    return ('0' + n).slice(-2);
  }

  function iso8601(date) {
    return date.getFullYear() +
      '-' + twoDigit(1 + date.getMonth()) +
      '-' + twoDigit(date.getDate()) +
      'T' + twoDigit(date.getHours()) +
      ':' + twoDigit(date.getMinutes());
  }

  function en_US(date) {
    var h = date.getHours() % 12;
    return MONTH_NAME[date.getMonth()] +
      ' ' + date.getDate() +
      ', ' + date.getFullYear() +
      '<br />' + (h == 0 ? 12 : h) +
      ':' + twoDigit(date.getMinutes()) +
      ' ' + (date.getHours() < 12 ? 'am' : 'pm');
  }

  var timeEl = document.getElementById('time');
  if (timeEl !== null) {
    var now = new Date();
    var div = document.createElement('div');
    div.innerHTML = en_US(now);
    div.setAttribute('datetime', iso8601(now));
    timeEl.append(div);
  }
};
setInterval(showTime, 1000);
<div id="time"></div>
Nidhin Joseph
  • 9,981
  • 4
  • 26
  • 48
1

To answer your third question, please try the code below

(function() {
  let elementsArray = document.querySelectorAll(".close");

  elementsArray.forEach(function(elem) {
    elem.addEventListener("click", function() {
      this.classList.add('hide');
    });
  });
})();
.close {
  background: #ccc;
  padding: 1em;
  margin: 1em;
}

.hide {
  display: none;
}
<div class="close">
  Text 1
</div>

<div class="close">
  Text 2
</div>

<div class="close">
  Text 3
</div>
Nidhin Joseph
  • 9,981
  • 4
  • 26
  • 48
  • Will this work when the divs are generated from the output? – Ty H Jun 29 '19 at 02:11
  • can you please be more specific to what you mean by *output*? – Nidhin Joseph Jun 29 '19 at 02:12
  • I am trying to have the box such as how you made your divs. Be once that are generated to the page as an output, but that retains the ability to close them. Hopefully that makes sense. – Ty H Jun 29 '19 at 02:19
  • I am sorry, but a fiddle would explain more. In short, let's say you have 3 divs, the click event is attached only to those three. If you add any div dynamically, you will need to attach click events to them on the go. Do you want me to do a fiddle or is it clear? – Nidhin Joseph Jun 29 '19 at 02:23
0

Answer to fist question

SecondDIVOutput.innerHTML+=   "<ul><li><time id='time'></time><br /><br />"+ output +"<br /><br /><span class='close'>&times;</span></li></ul>";

Changed to

SecondDIVOutput.innerHTML =   "<ul><li><time id='time'></time><br /> <br />"+ output +"<br /><br /><span class='close'>&times;</span></li></ul>" + SecondDIVOutput.innerHTML;

Note the += been changed to = and +SecondDIVOutput.innerHTML; added to the end. Also be aware this method is creating a new <ul> per item inserted. Correct solution at bottom.

Second Question

The time stamp issue is due to var timeEl = document.getElementById('time'); returning the first matching element in the DOM and you have multiple id="time" in DOM.

Last question

Each time you use SecondDIVOutput.innerHTML += ... all existing events within the SecondDIVOutput elements are lost. After calling convertOutput() you should run that addEventListener() loop immediately after it

Now the correct way of adding items to the list:

HTML:

<p>History log:</p><br><div style="white-space:pre-wrap"><ul id="outputListItem"></ul></div> 

JS:

function convertOutput(){
   var output = document.getElementById("output").value;
   var li = document.createElement('li');
   var currentTime = getCurrentTime();

   li.innerHTML = "<time id='time'>" + currentTime + "</time><br /> <br />"+ output +"<br /><br /><span class='close'>&times;</span>";
   document.getElementById('outputListItem').prepend(li);
}

function getCurrentTime() {
    var today = new Date();
    var HHMMSS = today.getHours() + ":" + today.getMinutes() + ":" + today.getSeconds();
    return HHMMSS;
}

You have to create a function to get the currentTime() bear in mind <time id='time'> needs to be unique if you want to dynamically change this as a timer.

See https://jsfiddle.net/h0xucobp/ for working example

John
  • 3,716
  • 2
  • 19
  • 21
  • The first change you made removing the += worked flawlessly, that’s awesome. However when I tired using your code for the correct way to add items, it doesn’t seem to be working for me. When I run that code it doesn’t output anything. Am I missing something? Also how would I do that function for the time? – Ty H Jun 29 '19 at 02:04
  • The html portion you have provided has change did you copy this bit? Also, change var currentTime = getTime(); to something like var currentTime = makeCurrentTime(); then create a function that returns the current time / date as a string. – John Jun 29 '19 at 02:36
  • I did copy that part to, I think it must be an issue with the time per. I’m still really new and novice at JavaScript. I’m not entirely sure how to make that work as you describe. – Ty H Jun 29 '19 at 02:52
  • 1
    I've slightly modified the code to include getCurrentTime() and added a jsfiddle showing it does work. – John Jun 29 '19 at 10:03
  • Quick question, I am trying to do as you suggested by calling to convert output, but having trouble getting it to work. I will update my question with the script I have been trying to get to work – Ty H Jul 04 '19 at 21:39
  • 1
    I presume you are trying to get the close working. New JS fiddle https://jsfiddle.net/efog91jq/ shows this working. Also, make sure todayDateTime() exists and always check console for any error messages. This will tell you exactly which line is failing. – John Jul 04 '19 at 22:55
  • Awesome man, that did it exactly! Thanks, Appreciate it. – Ty H Jul 05 '19 at 21:52