Tips for positioning a chat box at the bottom of a v-card's visible area

My goal is to create a chat app using Vuejs 3 and Vuetify 3, but I'm encountering an issue aligning the chatbox with the v-card component. Instead of being positioned at the bottom of the v-card, the chatbox (green) appears at the bottom of the page. You can see a visualization of this problem in the following screenshot: https://i.stack.imgur.com/ZAlXE.png

Please refer to the GitHub link for reproduction instead: https://github.com/samzhangjy/observerx-web

I am looking for a solution to have the chatbox aligned inside the v-card and pinned at the bottom even when scrolling, essentially keeping it within the v-card. Additionally, I would like to know if there is a built-in way to make the v-toolbar (the orange header) sticky at the top.

Thank you so much in advance!


If you prefer not to use CodeSandbox, here is the code related to this:

  • MainContainer.vue:
<script lang="ts" setup>
import { useTheme } from "vuetify";

const theme = useTheme();
const changeTheme = () =>
  (theme.global.name.value = theme.global.current.value.dark
    ? "light"
    : "dark");
</script>

<template>
  <v-container class="h-screen py-md-12 py-sm-5">
    <v-row justify="space-around" class="h-100">
      <v-card max-width="1000" height="100%" width="100%" class="overflow-auto">
        <v-toolbar color="tertiary-container" class="position-sticky">
          <template v-slot:prepend>
            <v-btn icon="$menu" color="on-tertiary-container"></v-btn>
          </template>

          <v-toolbar-title class="text-h6"> ObserverX </v-toolbar-title>

          <template v-slot:append>
            <v-btn
              icon="mdi-theme-light-dark"
              color="on-tertiary-container"
              @click="changeTheme"
            ></v-btn>
          </template>
        </v-toolbar>

        <v-card-text class="overflow-auto">
          <slot />
        </v-card-text>
      </v-card>
    </v-row>
  </v-container>
</template>

<style scoped></style>
  • ChatPage.vue:
<template>
  <v-container class="h-100">
    <v-responsive class="align-center text-center h-100">
      <MainContainer class="container h-100">
        <div class="messages">
          <p class="text-body-1" v-for="i in 100">Line #{{ i }}</p>
        </div>
        <div class="message-input-container">
          <v-textarea
            variant="solo"
            base-color="surface"
            bg-color="surface-variant"
            color="on-surface-variant"
            class="rounded-t-0 message-input"
            rows="3"
            no-resize
            rounded="0"
            v-model="currentMessage"
          >
            <template v-slot:append-inner>
              <v-btn
                icon="mdi-send"
                variant="tonal"
                size="small"
                color="primary"
                @click="sendMessage"
              ></v-btn>
            </template>
          </v-textarea>
        </div>
      </MainContainer>
    </v-responsive>
  </v-container>
</template>

<script lang="ts" setup>
import { ref } from "vue";
import MainContainer from "./MainContainer.vue";

const currentMessage = ref("");

const sendMessage = () => {
  console.log(currentMessage.value);
};
</script>

<style scoped>
.container {
  box-sizing: border-box;
  position: relative;
  overflow: hidden;
  padding-bottom: 100px;
  max-height: 1000px;
  height: 100%;
}

.messages {
  width: 100%;
  height: 100%;
  overflow: auto;
}

.message-input {
  max-width: 1000px;
  width: 100%;
  height: 100px;
  position: fixed;
  bottom: 0;
  left: 0;
  resize: none;
}

.message-input-container {
  position: absolute;
  max-width: 1000px;
  width: 100%;
}
</style>

Answer №1

Utilizing the position: fixed; property will ensure that the element remains fixed on the screen; you may want to experiment with using position: absolute; instead.

To revise your code, consider the following adjustments:

.message-input {
  max-width: 1000px;
  width: 100%;
  height: 100px;
  position: absolute;
  bottom: 0;
  left: 0;
  resize: none;
}

Additionally, there seems to be an issue with the CodeBox link provided, as I have encountered difficulty accessing your project.

Answer №2

If you want to create a layout where the messages list dynamically takes up the space between the toolbar and message input, you can achieve this by using flexbox and setting a fixed height for the container. This way, the messages will be displayed in an overflow-auto container within the card.

<v-card height="..." class="d-flex flex-column">
  <v-toolbar></v-toolbar>

  <div class="overflow-auto">
    <v-list :items="messages" dense></v-list>
  </div>
        
  <div class="message-input-container">...</div>
</v-card>

Check out the example code snippet:

const { createApp, ref } = Vue;
const { createVuetify } = Vuetify
const vuetify = createVuetify()
const app = {
  setup(){
    return {
      messages: ref(Array(50).fill(null).map((_,i) => `Message ${i}`))
    }
  }

}
createApp(app).use(vuetify).mount('#app')
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/vuetify@3/dist/vuetify.min.css" />
<link href="https://cdn.jsdelivr.net/npm/@mdi/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="62040d0c1622574c1a">[email protected]</a>/css/materialdesignicons.min.css" rel="stylesheet">
<div id="app">
  <v-app>
    <v-main>
      <v-card height="200px" class="ma-1 d-flex flex-column">
        <v-toolbar color="amber">
          <v-toolbar-title class="text-h6"> ObserverX </v-toolbar-title>
        </v-toolbar>

        <div class="overflow-auto">
          <v-list :items="messages" dense></v-list>
        </div>
        
        <div class="bg-green pa-3">Placeholder for Message Input</div>
      </v-card>
    </v-main>
  </v-app>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify@3/dist/vuetify.min.js"></script>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Iterate through the form fields and save the information into an object

I am attempting to create a JavaScript object by looping through form inputs. const data = {}; // loop through each input found in form $('#form_name').filter(':input').each(function() { const $id = $(this).attr('id'); c ...

Encountering a 403 Forbidden error while attempting to access a website built with Next.js

Every time I try to access the Next.JS website that's been deployed on IIS using the HTTP address, I'm faced with this error message: Get http://...../_next/static/chunks/webpack-73760e2208a549db.js net:: ERR_ABORTED 403 (Forbidden) However, w ...

Conceal a VueJS element using the v-if directive

I have developed a website where upon login, the username is stored in localStorage. This functionality is implemented in the Login.Vue file. login() { axios.post('/login', this.user) .then((response) => { alertify.set('notifier& ...

Learn how to use Vue to check for loaded resources such as initiators and assets. If all resources are loaded, the console will display "all resources done

What is the method to check if loading a resource (initiator, assets) returns a console.log("all resources done")? Click here for image description ...

Changing the 'badge' to 'panel' within the UI framework of 'ant design' has been set

Can the Badge be placed next to 'Info' in a Panel using ant design? View Code <div> <Collapse> <Panel header="Info" key="1"> <Badge count={4} style={{ backgroundColor: "#fff", ...

Customizing AxiosRequestConfig with Axios in TypeScript can greatly enhance the functionality and

Currently working with React and Axios. Lately, I've been experimenting with custom configurations in Axios as shown below: import $axios from 'helpers/axiosInstance' $axios.get('/customers', { handlerEnabled: false }) However, wh ...

Tips for integrating external libraries into VueJS CLI 3

I have recently created a Vue app using the latest Vue CLI (version 3.5.1) by running the following command: vue ui Here is my current folder structure: app_vue: |- node_modules |- public |- src: |---- assets |---- components: |---------Li ...

What could be causing the text-end to fail in Bootstrap 5?

Why can't I align the text to the right? Feeling frustrated as a beginner. <div class="top-bar"> <div class="container"> <div class="col-12 text-end"> <p><a href="tel:06 ...

Change the input field to uppercase using JavaScript but do not use inline JavaScript

Hey there! I have a basic script set up (see below) with an input field allowing users to enter a query. This query is then sent to a socrata webservice to request specific data, which is displayed in an alert box. So far, everything works smoothly. var l ...

Allow for separation amongst my cellular components

I have a table with multiple cells and I would like to add some spacing between each <td> element. You can find an example of my code on JSFIDDLE. .line { border-bottom:1px solid black; } .textRotation { -webkit-transform: rotate(-90deg); ...

Vue with DevXtreme DataGrid

Currently, I am working on creating a table using DataGrid in Vue. My goal is to populate the table with data from a v-for loop. However, I am facing some challenges in displaying the values such as {{user.name}}, {{user.email}}, and {{rol.name}}. As a beg ...

"Disabling Click Event on Sidebar Menu in Angular: A Step-by-Step Guide

I am working on an Angular project with a sidebar that I want to modify in order to disable click events on the menu items. My goal is to guide users through a specific flow without allowing them to navigate freely. However, simply disabling the [routerLin ...

Setting the `setCustomValidity()` method for dynamically inserted HTML elements Explanation on how to use

I am dynamically adding elements to my view and setting their attributes using the jQuery attr function as demonstrated in the code snippet below: var input = $("<input type='text' name='"+inputFieldName+"' '>"); input.attr( ...

Intersecting interactions: Mouse events on flex boxes

I am facing a challenge in my React application where I have two panels with buttons displayed using flex properties. Below is a simplified version of the HTML/SCSS code that illustrates the setup: <div class="bottom-panel left"> <butt ...

`Automatic toggling between two inputs with adjustable settings`

There are 2 input fields in my code that only accept positive floats with 2 decimals. Any other characters entered should be automatically removed using the change() function. Whenever the value of one input is changed, the value of the other input should ...

Despite the value being true, the if statement does not work when used with a Mongoose object in Node.js

Currently working on a bidding app, Below is the schema for the bidding model const bidSchema = new mongoose.Schema({ name: String, price : Number, description: String, location: String, specilization: String, image: String, ...

What is the method to reset a vuetify v-input-file?

I am attempting to reset my v-input-file when a button is clicked, but I am having trouble finding a function to accomplish this. I have tried the following commands: (upload_json is the name of my v-input-file) this.$refs.upload_json.files=[] this.$refs.u ...

What is the process of dynamically creating a Javascript object?

I have been experimenting with creating the following custom array: var deal_info = { "1": { "deal": { "deal_id": "1", "qty": "1", "option_price_sale": "7900", "price_ship": "2500", " ...

Retrieve the variance between two arrays and store the additions in AddedList and the removals in RemovedList using typescript

I am still getting the hang of Typescript and I am trying to figure out the best solution for my issue. I have two arrays, A and B, and I need to identify the difference between them in relation to array A. The goal is to separate the elements that were ad ...

Discovering the country associated with a country code using ngx-intl-tel-input

In my application, I am trying to implement a phone number field using this StackBlitz link. However, I have observed that it is not possible to search for a country by typing the country code (e.g., +231) in the country search dropdown. When I type a coun ...