This is the fifth day of my participation in the August More text Challenge. For details, see:August is more challenging

A, requirements,

When a program is running in the back end, the front end often displays a dynamic progress bar, or a loading animation, or a percentage number

Even better, of course, is to show the current user the details of how the program is running, such as the logs logged during the run, or the process in real time

Such as

[12:01:59] The application process is starting. [12:02:00] The process is successfully started. Obtaining network resources... [12:02:01] The download task is successfully started. [12:02:02] Download.... [12:02:03] Network resources are being parsed. [12:02:04] Related programs are being installed. [12:02:05] The installation is successful, and the application process is terminatedCopy the code

Of course, I’m just writing to show you what we want the log to look like. When there is a lot of log content or process content, the best thing to do is to have the page automatically scroll down as the content increases, so that the user can see the latest news

Second, the analysis

In this process, on the one hand, we need to constantly call the interface to get the latest data, on the other hand, we need to render the data to the page, and let the page scroll

Note that there are three conditions to make the page scroll

  1. The interface cannot return full data each time to directly replace the page data, which would result in a page without scrolling animation
  2. The CSS style of the page is set correctly to ensure that content only scrolls within the viewable area of the parent element and not the entire page
  3. Use the correct API to complete the scrolling effect

Three, the implementation method

  • The first condition is well done and we can use itsetInterval
var timer = setInertval(() = >{
    getLogList().then(res= >{
        randerView(res.data)
    })
},2000)
Copy the code

However, this method has a disadvantage, that is, when the user network is not smooth, or the server is crowded, the interface that has been called is always in the state of pendding, and the later interface is continuously called, which will make the congested service even worse

So I prefer to use setTimeout

async function getLogData(){
    const logData = await getLogList()
    randerView(logData.data)
    setTimeout(getLogData, 2000)
}
getLogData()
Copy the code
  • For the second style problem, you only need to correctly add a fixed height and overflow-y: Scroll to the parent element.

  • Let’s move on to the third question, how do I get things to scroll down automatically, because this is the most important part, so I’ll do it in a separate section

Discussion on the scheme of automatic page scrolling

In general, there are two ways to get content to scroll down automatically.

scrollIntoView

The scrollIntoView method scrolls the parent container of the element, making the element that called scrollIntoView visible in the parent element.

The syntax is as follows:

element.scrollIntoView(); // Equal to Element.scrollintoView (true) element.scrollintoView (alignToTop); // Equal to Element.scrollintoView (true) Element.scrollintoView (alignToTop); // Boolean element. ScrollIntoView (scrollIntoViewOptions); // This parameter is of type Object

Parameter description:
parameter type The default value note
alignToTop boolean true Is the top of the element aligned with the top of the visible area of the scroll area in which it is located
scrollIntoViewOptions object {behavior: “auto”, block: “start”, inline: “nearest”} Behavior: Defines a scrolling animation transition, optionalautosmooth;

Block: Defines vertical alignment. This option is optionalstart.center.endOr,nearest;

Inline: Specifies horizontal alignment. Optionalstart.center.endOr,nearest
Usage:
<template>
  <div >
    <strong>The process log</strong>
    <div style="max-height:120px; position:relative">
      <div v-if="logs.length">
        <p
          class="logList-item"
          v-for="(item, index) in logs"
          :key="index"
          :id="(logs.length === index+1)? 'scollLog':''"
        >{{ item }}</p>
      </div>
      <p v-else>Temporarily no data</p>
    </div>
  </div></template> <script lang='ts'> import { Component, Vue, Watch, Prop } from 'vue-property-decorator' import { formatTime } from '@/utils' @Component export default class extends Vue { @Prop() private LOGS: Array<object>; private name: string = 'processLog'; private logs: Array<string> = []; Private getData () {this.logs = this.logs? this.LOGS.map( (item: object): string => '[' + formatTime(item.updatedTime) + '] ' + item.content + '\n' ) : []} @watch ('LOGS') scrollLogs (newValue) {this.getData() // After log rendering, $nextTick(() => {if (newvalue.length! == 0) { (document.getElementById('scollLog') as HTMLElement) .scrollIntoView({ behavior: 'smooth', block: 'nearest' }) } }) } mounted () { this.getData() } } </script> <style lang="scss" scoped> .logList-item { padding: 8px 0; margin: 0; } </style>Copy the code
conclusion

ScrollIntoView is not very friendly to ios, Safari and IE. What’s wrong with other browsers

In addition, this method has no requirements on the layout, it is simple and easy to understand, and only needs to be called for the last rendered log

scrollTo

This is a commonplace method for scrolling content to specified coordinates.

The syntax is as follows:

scrollTo(xpos,ypos)

Parameter description:
parameter type The default value note
xpos number A necessity. The x coordinate of the document to be displayed in the upper-left corner of the window document display area
ypos number A necessity. The y coordinate of the document to be displayed in the upper-left corner of the window document display area
Usage:
<template>
  <div class="console-wraper">
      <div class="console_window" ref="consoleWindow">
        <div id="console_output" ref="consoleOutput">< / > < / > < / > < / ></div>
      </div>
  </div>
</template>

<script lang='ts'>
import { Component, Vue, Watch, Prop } from 'vue-property-decorator'
import { post } from '@/utils/http'

@Component
export default class extends Vue {
  
    async getData() {
        const res = await post(`/api/get/log`, { read: true })
        this.formatData(res.data)
    }
    
    formatData(data) {
      try {
        if (data.length) {
          data.forEach(item= > {
            this.printLine('[' + item.updateTime + '] ' + item.value)
          })
        } else {
            this.printLine('No data yet'.'input_line')}}catch (e) {
        console.log('error')}}printLine(s, type) {
      if ((s = String(s))) {
        let n = document.createElement('pre')

        if(! type) type ='output_line'
        
        if(n.innerText ! = =undefined) {
          // IE has to use innerText
          n.innerText = s
        } else {
          // Firefox uses createTextNode
          n.appendChild(document.createTextNode(s))
        }

        n.className = type
        this.$refs.consoleOutput.appendChild(n)
        // After adding the log, let the parent element scroll to its own distance
        this.$refs.consoleWindow.scrollTop = this.$refs.consoleWindow.scrollHeight
      }
    }
    
    mounted () {
      this.getData()
    }
}
</script>
<style lang=scss scoped>
.console-wraper{
    display: flex;
    flex-direction: column;
    height: 100%;
}
.console_window {
    flex: 1;
    background: # 333;
    overflow: auto;
    .console_output {
        white-space: pre-wrap;
        word-wrap: break-word;
        padding: 12px;
        font-weight: bold;
    }
    /deep/ pre.input_line {
        font-size: 14px;
        color: yellow;
        opacity: 0.8;
        padding: 0 20px;
    }
    /deep/ pre.output_line {
        color: #fff;
        font-size: 13px;
        white-space: pre-wrap;
        word-wrap: break-word;
        padding-left: 40px;
        opacity: 0.7; }}</style>
Copy the code

This is the end of this article, the case code has not been fully tested, have any questions in the comments section oh 🙂