1

My goal is to show a continuous console output on a webpage.

The container that holds the output has a maximum height. Every new line gets added to the bottom. Now I want the last line to always be visible for the user (unless he scrolls himself, then the scrollbar should not jump back, when a new line is added).

I tried to do that with CSS and while it works fine within chrome, it does not within Firefox:

jQuery(document).ready(function($) {
  window.setInterval(function() {
    $('.log').append('<div class="line">Lorem ipsum dolor sit amet</div>');
  }, 100);
});
.window {
  height: 300px;
  background: #fff;
}

.log {
  background-color: #1c1c1c;
  margin-top: 0;
  margin-bottom: 0;
  font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
  font-size: 12px;
  padding: 16px;
  overflow: auto;
  line-height: 1.45;
  border-radius: 3px;
  white-space: pre-wrap;
  overflow-wrap: break-word;
  color: #DDD;
}

.log-container {
  flex-basis: 100%;
}

.autoscroll {
  flex-basis: auto;
  display: flex;
  flex-direction: column-reverse;
  height: calc(100% - 56px);
  overflow-y: auto;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<div class="window">
  <h4>Show Logs</h4>
  <div class="autoscroll">
    <div class="log-container">
      <pre class="log"></pre>
    </div>
  </div>
</div>

(fiddle)

Does anybody have any idea how I can get this working reliably on all browsers?

Edit: It's not a duplicate of this question, as I want a css solution and not a javascript solution (if possible). It almost works, but sadly not yet within firefox.

MTCoster
  • 5,868
  • 3
  • 28
  • 49
Zoker
  • 2,020
  • 5
  • 32
  • 53
  • Yes but I don't want a javascript solution (if possible), but rather a css solution – Zoker Feb 19 '19 at 14:45
  • 4
    I'd say it's impossible using CSS alone and I say this about very few issues (you'll be amazed at what CSS can do). You're looking at JavaScript and you should update your question with your most relevant research and best coding attempt. As far as advancement in your task, you haven't shown anything. So it would be fair to say it doesn't work because you haven't done anything in that regard. – tao Feb 19 '19 at 14:47
  • @MarkBaijens Yes that's exactly what I was doing, but still it does not work on firefox – Zoker Feb 19 '19 at 14:48
  • @MarkBaijens, how does that work upon adding new content? – tao Feb 19 '19 at 14:49
  • Here is even a comment, saying that this wont work in firefox: https://stackoverflow.com/questions/18614301/keep-overflow-div-scrolled-to-bottom-unless-user-scrolls-up/44051405#comment80449724_44051405 – Zoker Feb 19 '19 at 14:50
  • Didn't test it, removed my comment. I'm pretty sure you will need Javascript for it then. Any reason why you don't want to use js? – Mark Baijens Feb 19 '19 at 14:50
  • Because I guess it's way more complicated. First I need to find out, if the output is higher than the container, then I need to catch if the user scrolls on his own and don't scroll the console anymore. The css solution seemed so much cleaner – Zoker Feb 19 '19 at 15:00
  • It's not complicated **at all**. Out of all things JS, this is probably one of the simplest. You place a [MutationObserver()](https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver) - that's the "complex" part on your div and [scroll to its bottom](https://stackoverflow.com/questions/270612/scroll-to-bottom-of-div) in the observer callback - the easy part. – tao Feb 19 '19 at 15:03
  • And how do I prevent the "scroll to bottom" when the user scrolls to the div himself? – Zoker Feb 19 '19 at 15:08
  • @Zoker If you add the content with Javascript, you can check at the moment you add the content if the div is already scrolled to the bottom. If not add the content and auto scroll, else just add the content. – Mark Baijens Feb 19 '19 at 15:09
  • Whenever user scrolls up you set a global scope flag. You wrap the code in your callback in a condition based on the global flag. When the user scrolls back down, you set your flag accordingly, so that the autoscroll starts happening on mutations again. – tao Feb 19 '19 at 15:10

2 Answers2

2

I don't know of any way to do this without using JavaScript, but in case you'd like that solution, this is a method I have used for exactly what you are looking for.

jQuery.fn.scrollTo = function(elem) {
    if( this[0].scrollTop > this[0].scrollHeight - this[0].offsetHeight - $(elem).height() - 10) {
        $(this).scrollTop($(this).scrollTop() - $(this).offset().top + $(elem).offset().top);
    }
    return this; 
};

jQuery(document).ready(function($) {
  var i = 1;
  window.setInterval(function() {
    var element = $('<div class="line">Lorem ipsum dolor sit amet ' + i + '</div>');
    $('.log').append(element).scrollTo(element);
    i++;
  }, 100);
});
.window {
  height: 300px;
  background: #fff;
}

.log {
  background-color: #1c1c1c;
  margin-top: 0;
  margin-bottom: 0;
  font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
  font-size: 12px;
  padding: 16px;
  overflow: auto;
  line-height: 1.45;
  border-radius: 3px;
  white-space: pre-wrap;
  overflow-wrap: break-word;
  color: #DDD;
}

.log-container {
  flex-basis: 100%;
}

.autoscroll {
  flex-basis: auto;
  display: flex;
  flex-direction: column-reverse;
  height: calc(100% - 56px);
  overflow-y: auto;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<div class="window">
  <h4>Show Logs</h4>
  <div class="autoscroll">
    <div class="log-container">
      <pre class="log"></pre>
    </div>
  </div>
</div>
hawkstrider
  • 4,141
  • 16
  • 27
1

This is an example how you can do it with js.

setInterval(function(){ 
  logTime(); 
}, 1000);

var consoleDiv = document.getElementById('timelog');

function logTime(){
  //check if div is scrolled to the bottom
  var atBottom = isElementScrolledToBottom(consoleDiv);
  
  //add content
  var timeNode = document.createElement("p");
  var timeText = document.createTextNode(Date.now());
  timeNode.appendChild(timeText);
  consoleDiv.appendChild(timeNode);
  
  //if div was at the bottom, scroll to bottom again.
  if(atBottom) {
    scrollToBottom(consoleDiv);
  }
};

//function to check if element is scrolled to the bottom
function isElementScrolledToBottom(el) {
  if (el.scrollTop >= (el.scrollHeight - el.offsetHeight)) {
      return true;
  }
  return false;
}

//function to scroll to bottom
function scrollToBottom(el) {
  el.scrollTop = el.scrollHeight;
}
#timelog {
  height: 200px;
  overflow-y: scroll;
}
<div id="timelog"></div>
Mark Baijens
  • 13,028
  • 11
  • 47
  • 73