I have a simple div with a v-for loop that displays items on a page. These items have a class on them which I would like to select using Javascript's querySelector method.
<div class="product-feed">
<div v-for="Product in ProductFeed" :key="Product.ProductID" class="product-item" >
<Product-Vue-Component :Product="Product"></Product-Vue-Component>
</div>
</div>
<script>
...
setup() {
async function loadFeed(){
await nextTick();
let element1 = document.querySelector('.product-feed');
console.log(`element1`, element1) // this works and displays the element
let element2 = document.querySelector('.product-item:last-of-type');
console.log(`element2`, element2) // this comes back as null
}
}
onMounted(()=> {
loadFeed();
})
}
...
</script>
Even though I am waiting for the DOM to render using nextTick(), the function loadFeed() cannot pick up on any item that is in the v-for loop.
I need to detect items in the v-for loop so that I can implement an infinite scroll feature where more items are loaded as the user scrolls to the bottom of the list of .product-item elements (hence :last-of-type pseudo selector)
Where is it going wrong and is it possible to select elements this way?
Try to set ref on div with class="product-item"
<div ref="el" v-for="Product in ProductFeed" :key="Product.ProductID" class="product-item" >
then in setup function :
const el = ref(null)
onMounted(() => {
loadFeed()
})
async function loadFeed(){
await nextTick();
let element1 = document.querySelector('.product-feed');
console.log(`element1`, element1) // this works and displays the element
console.log(el.value)
}
}
return {el}
Please, use refs for that just add ref attribute to the container ref="productFeed" and get this element with
import { ref } from 'vue'
...
const productFeed = ref(null)
...
return {
productFeed
}
Then you can use productFeed.value as a variable, containing dom element
Try to use refs like this:
<div
v-for="Product in ProductFeed" :ref="`product--${Product.id}`">
</div>
Related
As a novice Javascript programmer, I'd like to create an html document presenting a feature very similar to the "reveal spoiler" used extensively in the Stack Exchange sites.
My document therefore has a few <div> elements, each of which has an onClick event listner which, when clicked, should reveal a hiddent text.
I already know that this can be accomplished, e.g., by
<div onclick="this.innerHTML='Revealed text'"> Click to reveal </div>
However, I would like the text to be revealed to be initially stored in a variable, say txt, which will be used when the element is clicked, as in:
<div onclick="this.innerHTML=txt"> Click to reveal </div>
Since there will be many such <div> elements, I certainly cannot store the text to be revealed in a global variable. My question is then:
Can I declare a variable that is local to a specific html element?
Yes you can. HTML elements are essentially just Javascript Objects with properties/keys and values. So you could add a key and a value to an HTML element object.
But you have to add it to the dataset object that sits inside the element, like this:
element.dataset.txt = 'This is a value' // Just like a JS object
A working example of what you want could look like this:
function addVariable() {
const myElement = document.querySelector('div')
myElement.dataset.txt = 'This is the extended data'
}
function showExtendedText(event) {
const currentElement = event.currentTarget
currentElement.innerHTML += currentElement.dataset.txt
}
addVariable() // Calling this one immediately to add variables on initial load
<div onclick="showExtendedText(event)">Click to see more </div>
Or you could do it by adding the variable as a data-txt attribute right onto the element itself, in which case you don't even need the addVariable() function:
function showExtendedText(event) {
const currentElement = event.currentTarget
currentElement.innerHTML += currentElement.dataset.txt
}
<div onclick="showExtendedText(event)" data-txt="This is the extended data">Click to see more </div>
To access the data/variable for the specific element that you clicked on, you have to pass the event object as a function paramater. This event object is given to you automatically by the click event (or any other event).
Elements have attributes, so you can put the information into an attribute. Custom attributes should usually be data attributes. On click, check if a parent element has one of the attributes you're interested in, and if so, toggle that parent.
document.addEventListener('click', (e) => {
const parent = e.target.closest('[data-spoiler]');
if (!parent) return;
const currentMarkup = parent.innerHTML;
parent.innerHTML = parent.dataset.spoiler;
parent.dataset.spoiler = currentMarkup;
});
<div data-spoiler="foo">text 1</div>
<div data-spoiler="bar">text 2</div>
That's the closest you'll get to "a variable that is local to a specific html element". To define the text completely in the JavaScript instead, one option is to use an array, then look up the clicked index of the spoiler element in the array.
const spoilerTexts = ['foo', 'bar'];
const spoilerTags = [...document.querySelectorAll('.spoiler')];
document.addEventListener('click', (e) => {
const parent = e.target.closest('.spoiler');
if (!parent) return;
const currentMarkup = parent.innerHTML;
const index = spoilerTags.indexOf(parent);
parent.innerHTML = spoilerTexts[index];
spoilerTexts[index] = currentMarkup;
});
<div class="spoiler">text 1</div>
<div class="spoiler">text 2</div>
There are also libraries that allow for that sort of thing, by associating each element with a component (a JavaScript function/object used by the library) and somehow sending a variable to that component.
// for example, with React
const SpoilerElement = ({ originalText, spoilerText }) => {
const [spoilerShown, setSpoilerShown] = React.useState(false);
return (
<div onClick={() => setSpoilerShown(!spoilerShown)}>
{ spoilerShown ? spoilerText : originalText }
</div>
);
};
const App = () => (
<div>
<SpoilerElement originalText="text 1" spoilerText="foo" />
<SpoilerElement originalText="text 2" spoilerText="bar" />
</div>
)
ReactDOM.createRoot(document.querySelector('.react')).render(<App />);
<script crossorigin src="https://unpkg.com/react#18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#18/umd/react-dom.development.js"></script>
<div class='react'></div>
Thanks everybody for your answers, which helped immensely! However, as a minimalist, I took all that I learned from you and came up with what I believe is the simplest possible code achieving my goal:
<div spoiler = "foo" onclick="this.innerHTML=this.getAttribute('spoiler')">
Click for spoiler
</div>
<div spoiler = "bar" onclick="this.innerHTML=this.getAttribute('spoiler')">
Click for spoiler
</div>
I am using Django templating engine and JavaScript. My HTML looks like this
<p class="content-card__address">{{ z.formatted_address|truncatewords:6 }}</p>
<div class="content-card-inner">
<p class="content-card__review">Отзывы ({{ z.post_relate.all.count }})</p>
<p class="content-card__phone">{{ z.international_phone_number }}</p>
<div class="div-shaddow"></div>
<p class="content-card__text">Показать</p>
</div>
Cards with text to be generated on the backend using a template engine. My JavaScript code only works on the first card and I need it to work on all cards. With JavaScript I add a class to the div elements. Here is my JavaScript
let call = document.querySelector('.content-card__text');
let divShadow = document.querySelector('.div-shaddow');
call.addEventListener('click', clickCall)
function clickCall() {
call.classList.add('visually-hidden');
divShadow.classList.add('visually-hidden');
}
This code returns you the first element in the DOM and you add click handlers only for it
document.querySelector('.content-card__text')
It will work for you:
const buttons = document.querySelectorAll('.content-card__text');
buttons.forEach(button => {
button.addEventListener('click', clickCall);
});
But please also note that you need to take this into account when working with .divShadow if this element is not alone on the page
Update: example based on your comment
const buttons = document.querySelectorAll('.content-card__text');
const divShadow = document.querySelectorAll('.div-shaddow');
buttons.forEach((button, index) => {
button.addEventListener('click', () => clickCall(index));
});
function clickCall(index) {
buttons[index].classList.add('visually-hidden');
divShadow[index].classList.add('visually-hidden');
}
I'm trying to use Svelte to do some conditional styling and highlighting to equations. While I've been successful at applying a global static style to a class, I cannot figure out how to do this when an event occurs (like one instance of the class is hovered over).
Do I need to create a stored value (i.e. some boolean that gets set to true when a class is hovered over) to use conditional styling? Or can I write a function as in the example below that will target all instances of the class? I'm a bit unclear why targeting a class in styling requires the :global(classname) format.
App.svelte
<script>
// import Component
import Katex from "./Katex.svelte"
// math equations
const math1 = "a\\htmlClass{test}{x}^2+bx+c=0";
const math2 = "x=-\\frac{-b\\pm\\sqrt{b^2-4ac}}{2a}";
const math3 = "V=\\frac{1}{3}\\pi r^2 h";
// set up array and index for reactivity and initialize
const mathArray = [math1, math2, math3];
let index = 0;
$: math = mathArray[index];
// changeMath function for button click
function changeMath() {
// increase index
index = (index+1)%3;
}
function hoverByClass(classname,colorover,colorout="transparent")
{
var elms=document.getElementsByClassName(classname);
console.log(elms);
for(var i=0;i<elms.length;i++)
{
elms[i].onmouseover = function()
{
for(var k=0;k<elms.length;k++)
{
elms[k].style.backgroundColor=colorover;
}
};
elms[i].onmouseout = function()
{
for(var k=0;k<elms.length;k++)
{
elms[k].style.backgroundColor=colorout;
}
};
}
}
hoverByClass("test","pink");
</script>
<h1>KaTeX svelte component demo</h1>
<h2>Inline math</h2>
Our math equation: <Katex {math}/> and it is inline.
<h2>Displayed math</h2>
Our math equation: <Katex {math} displayMode/> and it is displayed.
<h2>Reactivity</h2>
<button on:click={changeMath}>
Displaying equation {index}
</button>
<h2>Static math expression within HTML</h2>
<Katex math={"V=\\pi\\textrm{ m}^3"}/>
<style>
:global(.test) {
color: red
}
</style>
Katex.svelte
<script>
import katex from "katex";
export let math;
export let displayMode = false;
const options = {
displayMode: displayMode,
throwOnError: false,
trust: true
}
$: katexString = katex.renderToString(math, options);
</script>
<svelte:head>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex#0.12.0/dist/katex.min.css" integrity="sha384-AfEj0r4/OFrOo5t7NnNe46zW/tFgW6x/bCJG8FqQCEo3+Aro6EYUG4+cU+KJWu/X" crossorigin="anonymous">
</svelte:head>
{#html katexString}
If I understand it correctly you have a DOM structure with arbitrary nested elements and you would want to highlight parts of the structure that share the same class.
So you would have a structure like this:
<div>
<p>This is some text <span class="a">highlight</span></p>
<span class="a">Another highlight</span>
<ul>
<li>Some listitem</li>
<li class="a">Some listitem</li>
<li class="b">Some listitem</li>
<li class="b">Some listitem</li>
</ul>
</div>
And if you select an element with class="a" all elements should be highlighted regardles where they are in the document. This arbitrary placement makes using the sibling selector in css not possible.
There is no easy solution to this, but I will give you my attempt:
This is the full code with some explanation
<script>
import { onMount } from 'svelte'
let hash = {}
let wrapper
onMount(() => {
[...wrapper.querySelectorAll('[class]')].forEach(el => {
if (hash[el.className]) return
else hash[el.className] = [...wrapper.querySelectorAll(`[class="${el.className}"]`)]
})
Object.values(hash).forEach(nodes => {
nodes.forEach(node => {
node.addEventListener('mouseover', () => nodes.forEach(n => n.classList.add('hovered')))
node.addEventListener('mouseout', () => nodes.forEach(n => n.classList.remove('hovered')))
})
})
})
</script>
<div bind:this={wrapper}>
<p>
Blablabla <span class="a">AAA</span>
</p>
<span class="a">BBBB</span>
<ul>
<li>BBB</li>
<li class="a b">BBB</li>
<li class="b">BBB</li>
<li class="b">BBB</li>
</ul>
</div>
<style>
div :global(.hovered) {
background-color: red;
}
</style>
The first thing I did was use bind:this to get the wrapping element (in your case you would put this around the {#html katexString}, this will make that the highlight is only applied to this specific subtree.
Doing a querySelector is a complex operation, so we will gather all the related nodes in a sort of hashtable during onMount (this kind of assumes the content will never change, but since it's rendered with #html I believe it's safe to do so).
As you can see in onMount, I am using the wrapper element to restrict the selector to this section of the page, which is a lot faster than checking the entire document and is probably what you want anyway.
I wasn't entirely sure what you want to do, but for simplicity I am just grabbing every descendant that has a class and make a hash section for each class. If you only want certain classes you could write out a bunch of selectors here instead:
hash['selector-1'] = wrapper.querySelectorAll('.selector-1');
hash['selector-2'] = wrapper.querySelectorAll('.selector-2')];
hash['selector-3'] = wrapper.querySelectorAll('.selector-3');
Once this hashtable is created, we can loop over each selector, and attach two event listeners to all of the elements for that selector. One mouseover event that will then again apply a new class to each of it's mates. And a mouseout that removes this class again.
This still means you have to add hovered class. Since the class is not used in the markup it will be removed by Svelte unless you use :global() as you found out yourself. It is indeed not that good to have global classes because you might have unintended effect elsewhere in your code, but you can however scope it as I did in the code above.
The line
div > :global(.hovered) { background-color: red; }
will be processed into
div.svelte-12345 .hovered { background-color: red; }
So the red background will only be applied to .hovered elements that are inside this specific div, without leaking all over the codebase.
Demo on REPL
Here is the same adapted to use your code and to use a document-wide querySelector instead (you could probably still restrict if wanted by having the bind one level higher and pass this node into the component)
Other demo on REPL
I'm trying to add an event listener on some repeating innerHTML. I.E for every lot of HTML added by innerHTML, I'll also need to add a click event onto it.
To complicate things I'm also importing a data set from another JS file imported under the name data. As you can see in the code I need the data inside the event listener to be specific to the for loop iteration of the innerHTML so that when I fire the event listener I can see the correct, corresponding data.
This is my attempt:
JS:
import data from './data.js';
import img from './images.js';
export const lists = () => {
const main = document.getElementById('main');
main.innerHTML = `
<div class="main-container">
<div class="flex-between row border-bottom">
<div class="flex new-list">
<img class="create-img img-radius" src="${img.symbols[0]}" alt="Delete Bin">
<h3>New List</h3>
</div>
<div class="flex-between sections">
<h3 class="text-width flex-c">Items:</h3>
<h3 class="text-width flex-c">Reminders:</h3>
<h3 class="text-width flex-end">Created:</h3>
</div>
</div>
<div id="lists"></div>
</div>
`;
const lists = document.getElementById('lists');
for (let i = 0; i < data.lists.length; i++) {
let obj = eval(data.lists[i]);
let totalReminders = getTotalReminders(obj);
lists.innerHTML += `
<div class="flex-between row list">
<h4>${obj.name}</h4>
<div class="flex-between sections">
<h4 class="number-width flex-c">${obj.items.length}</h4>
<h4 class="number-width flex-c">${totalReminders}</h4>
<div class="text-width flex-end">
<h4 class="date">${obj.created}</h4>
<img class="img-radius" src="${img.symbols[3]}" alt="Delete Bin">
</div>
</div>
</div>
`;
const list = document.querySelector('.list');
list.addEventListener('click', () => { // click event
listNav.listNav(obj.name);
listSidebarL.listSidebarL();
listSidebarR.listSidebarR();
listMain.listMain(obj.items);
});
};
};
const getTotalReminders = passed => { // find total reminders
let total = 0;
for (let i = 0; i < passed.items.length; i++) {
total += passed.items[i].reminders;
};
return total;
};
At the moment ONLY the first iteration of innerHTML += has an event listener attached and when I click on it I see the data that should be corresponding the last iteration.
What am I doing wrong here?
You need to move the code that sets up the event handlers so that it is outside of your for loop and runs after that loop is finished. Then, instead of .querySelector(), which only returns the first matching element, you need .querySelectorAll() to return all matching elements. After that, you'll loop through all those elements and set up the handler.
You'll also need to change how your obj variable is declared so that it will be in scope outside of the for loop. Do this by declaring it just before the loop, but assigning it inside the loop:
let obj = null; // Now, obj is scoped so it can be accessed outside of the loop
for (let i = 0; i < data.lists.length; i++) {
obj = eval(data.lists[i]);
And, put the following just after the for loop finishes:
// Get all the .list elements into an Array
const list = Array.prototype.slice.call(document.querySelectorAll('.list'));
// Loop over the array and assign an event handler to each array item:
list.forEach(function(item){
item.addEventListener('click', () => {
listNav.listNav(obj.name);
listSidebarL.listSidebarL();
listSidebarR.listSidebarR();
listMain.listMain(obj.items);
});
});
With all this said, your approach here is really not very good. There is almost always another option than to use eval() for anything and using .innerHTML is usually something to avoid due to its security and performance implications. Using it in a loop is almost always a bad idea. You really should be using the DOM API to create new elements, configure them and inject them into the DOM. If you must use .innerHTML, then build up a string in your loop and after the loop, inject the string into the DOM via .innerHTML, just once.
One options is to look at event delegation/bubbling. The basic principle here is you add the event handler to a parent object, in this case <div id="lists"></div>. Then when the event is fired you query the target of that event to see if it matches your element.
Using this technique you don't have to re-bind event handlers when new items are added, particularly useful if the items are added by user interaction.
In your case it would look something like:
export const lists = () => {
const main = document.getElementById('main');
main.innerHTML = `
<div class="main-container">
<div class="flex-between row border-bottom">
<div class="flex new-list">
<img class="create-img img-radius" src="${img.symbols[0]}" alt="Delete Bin">
<h3>New List</h3>
</div>
<div class="flex-between sections">
<h3 class="text-width flex-c">Items:</h3>
<h3 class="text-width flex-c">Reminders:</h3>
<h3 class="text-width flex-end">Created:</h3>
</div>
</div>
<div id="lists"></div>
</div>
`;
const lists = document.getElementById('lists');
//Now that the parent element is added to the DOM
//Add the event handler
lists.addEventListener("click",function(e) {
// e.target was the clicked element
if (e.target && e.target.matches(".list")) {
listNav.listNav(obj.name);
listSidebarL.listSidebarL();
listSidebarR.listSidebarR();
listMain.listMain(obj.items);
}
//Add Items etc
});
NOTE Scots comments re eval and innerHTML apply equally to this answer.
I have a Vue.js component with several elements in it. I want to automatically scroll to the bottom of that element when a method in the component is called.
Basically, do the same as this. However, I haven't found a way to get the element within my component and modify scrollTop
I'm currently using Vue.js 2.0.8.
2022 easy, readable, smooth scrolling ability, & won't hurt your brain... use el.scrollIntoView()
scrollIntoView() has options you can pass it like scrollIntoView({behavior: 'smooth'}) to get smooth scrolling out of the box and does not require any external libraries.
Here is a fiddle.
methods: {
scrollToElement() {
const el = this.$refs.scrollToMe;
if (el) {
// Use el.scrollIntoView() to instantly scroll to the element
el.scrollIntoView({behavior: 'smooth'});
}
}
}
Then if you wanted to scroll to this element on page load you could call this method like this:
mounted() {
this.scrollToElement();
}
Else if you wanted to scroll to it on a button click or some other action you could call it the same way:
<button #click="scrollToElement">scroll to me</button>
The scroll works all the way down to IE 8. The smooth scroll effect does not work out of the box in IE or Safari. If needed there is a polyfill available for this here as #mostafaznv mentioned in the comments.
As I understand, the desired effect you want is to scroll to the end of a list (or scrollable div) when something happens (e.g.: an item is added to the list). If so, you can scroll to the end of a container element (or even the page it self) using only pure JavaScript and the VueJS selectors.
var container = this.$el.querySelector("#container");
container.scrollTop = container.scrollHeight;
I've provided a working example in this fiddle.
Every time a item is added to the list, the list is scrolled to the end to show the new item.
I tried the accepted solution and it didn't work for me. I use the browser debugger and found out the actual height that should be used is the clientHeight BUT you have to put this into the updated() hook for the whole solution to work.
data(){
return {
conversation: [
{
}
]
},
mounted(){
EventBus.$on('msg-ctr--push-msg-in-conversation', textMsg => {
this.conversation.push(textMsg)
// Didn't work doing scroll here
})
},
updated(){ <=== PUT IT HERE !!
var elem = this.$el
elem.scrollTop = elem.clientHeight;
},
Use the ref attribute on the DOM element for reference
<div class="content scrollable" ref="msgContainer">
<!-- content -->
</div>
You need to setup a WATCH
data() {
return {
count: 5
};
},
watch: {
count: function() {
this.$nextTick(function() {
var container = this.$refs.msgContainer;
container.scrollTop = container.scrollHeight + 120;
});
}
}
Ensure you're using proper CSS
.scrollable {
overflow: hidden;
overflow-y: scroll;
height: calc(100vh - 20px);
}
Here is a simple example using ref to scroll to the bottom of a div.
/*
Defined somewhere:
var vueContent = new Vue({
el: '#vue-content',
...
*/
var messageDisplay = vueContent.$refs.messageDisplay;
messageDisplay.scrollTop = messageDisplay.scrollHeight;
<div id='vue-content'>
<div ref='messageDisplay' id='messages'>
<div v-for="message in messages">
{{ message }}
</div>
</div>
</div>
Notice that by putting ref='messageDisplay' in the HTML, you have access to the element through vueContent.$refs.messageDisplay
If you need to support IE11 and (old) Edge, you can use:
scrollToBottom() {
let element = document.getElementById("yourID");
element.scrollIntoView(false);
}
If you don't need to support IE11, the following will work (clearer code):
scrollToBottom() {
let element = document.getElementById("yourID");
element.scrollIntoView({behavior: "smooth", block: "end"});
}
Try vue-chat-scroll:
Install via npm: npm install --save vue-chat-scroll
Import:
import Vue from 'vue'
import VueChatScroll from 'vue-chat-scroll'
Vue.use(VueChatScroll)
in app.js after window.Vue = require('vue').default;
then use it with :
<ul class="messages" v-chat-scroll>
// your message/chat code...
</ul>
For those that haven't found a working solution above, I believe I have a working one. My specific use case was that I wanted to scroll to the bottom of a specific div - in my case a chatbox - whenever a new message was added to the array.
const container = this.$el.querySelector('#messagesCardContent');
this.$nextTick(() => {
// DOM updated
container.scrollTop = container.scrollHeight;
});
I have to use nextTick as we need to wait for the dom to update from the data change before doing the scroll!
I just put the above code in a watcher for the messages array, like so:
messages: {
handler() {
// this scrolls the messages to the bottom on loading data
const container = this.$el.querySelector('#messagesCard');
this.$nextTick(() => {
// DOM updated
container.scrollTop = container.scrollHeight;
});
},
deep: true,
},
The solution did not work for me but the following code works for me. I am working on dynamic items with class of message-box.
scrollToEnd() {
setTimeout(() => {
this.$el
.getElementsByClassName("message-box")
[
this.$el.getElementsByClassName("message-box").length -
1
].scrollIntoView();
}, 50);
}
Remember to put the method in mounted() not created() and add class message-box to the dynamic item. setTimeout() is essential for this to work. You can refer to https://forum.vuejs.org/t/getelementsbyclassname-and-htmlcollection-within-a-watcher/26478 for more information about this.
This is what worked for me
this.$nextTick(() => {
let scrollHeight = this.$refs.messages.scrollHeight
window.scrollTo(0, scrollHeight)
})
In the related question you posted, we already have a way to achieve that in plain javascript, so we only need to get the js reference to the dom node we want to scroll.
The ref attribute can be used to declare reference to html elements to make them available in vue's component methods.
Or, if the method in the component is a handler for some UI event, and the target is related to the div you want to scroll in space, you can simply pass in the event object along with your wanted arguments, and do the scroll like scroll(event.target.nextSibling).
I had the same need in my app (with complex nested components structure) and I unfortunately did not succeed to make it work.
Finally I used vue-scrollto that works fine !
My solutions without modules:
Template
<div class="scrollable-content" ref="conversations" />
Script
scrollToBottom() {
const container = this.$refs.conversations;
container.scrollTop = container.scrollHeight;
},
scrollToBottom() {
this.$nextTick(function () {
let BoxEl = document.querySelector('#Box');
if(BoxEl)
BoxEl.scrollTop = BoxEl.scrollHeight;
});
}
Agree with Lurein Perera
Just want to add extra info
watch: {
arrayName: {
handler() {
const container = this.$el.querySelector("#idName");
this.$nextTick(() => {
container.scrollTop = container.scrollHeight;
});
},
deep: true,
},
},
Where as:
arrayName = Name of array
idName = The id attribute has to be added to the div where you want the scrollbar to auto-scroll down when arrayName length increases.
scrollToElement() {
const element = this.$refs.abc; // here abc is the ref of the element
if (element) {
el.scrollIntoView({behavior: 'smooth'});
}
}
}
here you need to use ref for the particular div or element which you want make visible on scroll.
if you have a table and you want to locate the last row of the table then you have to use -
element.lastElementChild.scrollIntoView({behaviour:'smooth'})
Here not that if you ware asynchronously adding the element to the table then you have to take care of it. you can test it using setTimeout, if that is making any difference.
e.g.
const element = this.$refs.abc;
if (element) {
setTimeout(() => {
element.lastElementChild.scrollIntoView({behaviour:'smooth'})
}, 1000);
}
}
replace set timeout with your own async logic.
Using Composition API and TypeScript
I set the parameter scrollTop equal to scrollHeightfrom the HTMLDivElment API.
<template>
<div id="container" ref="comments">
Content ...
</div>
</template>
<script lang="ts">
import { defineComponent, ref, Ref, watchEffect } from 'vue'
export default defineComponent({
setup() {
const comments: Ref<null | HTMLDivElement> = ref(null)
watchEffect(() => {
if(comments.value) {
comments.value.scrollTop = comments.value.scrollHeight
}
})
return {
comments
}
}
})
</script>