Plain javascript ScrollTop is not working - javascript

When I get a new message or send a new message chat should scroll down. I handle that with this watcher (I update random value in Vuex and watch for changes within component for chat):
watch: {
scrollChatDown: function (val) {
if (this.$refs.chat !== undefined) {
this.$refs.chat.scrollTop = 9999999999999999999999
console.log('WORKING!')
}
}
}
I get this console.log in Mozilla but scrollTop is not working, is there any other solution for this? :D

See this one, uses the current height to scroll down
watch: {
scrollChatDown: function (val) {
if (this.$refs.chat !== undefined) {
this.$refs.chat.scrollTop = this.$refs.chat.scrollHeight
console.log('WORKING!')
}
}
}

Related

Tiles dont turn right

I am making a website, http://phaidonasgialis.xyz. I want to make the tiles to turn and every other tile back. Until now it works except one small bug, the tile that you pressed does not turn back until you click second time.Any help would be thankful.
Here is the code:
$(document).ready(function() {
var previous = [];
$('.flip-card').each(function(i, obj) {
$(this).click(function() {
if ($(this).find('.flip-card-inner').hasClass('flip-card-transform')) {
$(this).find('.flip-card-inner').removeClass('flip-card-transform');
} else {
$(this).find('.flip-card-inner').addClass('flip-card-transform');
previous.push(this);
if (previous.length >= 2 && previous[previous.length - 2] != previous[previous.length - 1]) {
for (var i = 0; i < previous.length; i++) {
if ($(this).find('.flip-card-inner').hasClass('flip-card-transform')) {
$(previous[i - 1]).find('.flip-card-inner').removeClass('flip-card-transform');
console.log("2")
} else {
$(this).find('.flip-card-inner').addClass('flip-card-transform');
console.log("3")
}
}
}
}
});
});
If I understand your question correctly, you would like a card to flip back automatically after a certain period of time? If so then you just should set a Timeout and remove the class flip-card-transform from .flip-card-inner.
Aside note: you should definitely optimize your code by using a variable instead of doing $(this).find('.flip-card-inner')– converting this to jQuery object and searching trough its children every time when you need your flip-card-inner. Also instead of $('.flip-card').each(...) you could directly use $('.flip-card').click(...). And also as Harun Yilmaz suggested in his comment you don't need previous as an array...
So something like this should work:
$(document).ready(function () {
var previous
var timeout
$('.flip-card').click(function () {
var cardInner = $(this).find('.flip-card-inner')
if (cardInner.hasClass('flip-card-transform')) {
cardInner.removeClass('flip-card-transform')
clearTimeout(timeout)
} else {
cardInner.addClass('flip-card-transform')
// Set a Timeout
timeout = setTimeout(function () {
cardInner.removeClass('flip-card-transform')
}, 2000) // set whatever time sutable for you
// Also as Harun Yilmaz suggested in his comment you don't need previous as an array
if (previous && previous.hasClass('flip-card-transform')) {
previous.removeClass('flip-card-transform')
}
previous = cardInner
}
})
})

How to convert javascript code for Angular

I'm trying to implement something like the following into an Angular project: https://codepen.io/vincentorback/pen/NGXjda
The code compiles just fine in VS code, but when I try and preview in the browser, I get the following two errors:
Uncaught (in promise): TypeError undefined is not an object (evaluating 'this.context.addEventListener')
TypeError undefined is not an object (evaluating 'this.getScrollPos')
Stackblitz: https://stackblitz.com/edit/ionic-rv4ju7
home.page.ts
export class HomePage implements OnInit {
context = document.getElementsByClassName('loop')[0];
startElement = document.getElementsByClassName('is-start')[0];
clones = document.getElementsByClassName('is-clone');
disableScroll = false;
scrollWidth;
scrollPos;
clonesWidth;
i;
constructor() {
window.requestAnimationFrame(this.reCalc);
this.context.addEventListener('scroll', function () {
window.requestAnimationFrame(this.scrollUpdate);
}, false);
window.addEventListener('resize', function () {
window.requestAnimationFrame(this.reCalc);
}, false);
}
getScrollPos() {
return (this.context.pageXOffset || this.context.scrollLeft) - (this.context.clientLeft || 0);
}
setScrollPos(pos) {
this.context.scrollLeft = pos;
}
getClonesWidth() {
this.clonesWidth = 0;
this.i = 0;
for (this.i; this.i < this.clones.length; this.i += 1) {
this.clonesWidth = this.clonesWidth + this.clones[this.i].clientWidth;
}
return this.clonesWidth;
}
reCalc() {
this.scrollPos = this.getScrollPos();
this.scrollWidth = this.context.scrollWidth;
this.clonesWidth = this.getClonesWidth();
if (this.scrollPos <= 0) {
this.setScrollPos(1);
}
}
scrollUpdate() {
if (this.disableScroll === false) {
this.scrollPos = this.getScrollPos();
if (this.clonesWidth + this.scrollPos >= this.scrollWidth) {
// Scroll to the left when you’ve reached the far right
this.setScrollPos(1); // Scroll 1 pixel to allow scrolling backwards.
this.disableScroll = true;
} else if (this.scrollPos <= 0) {
// Scroll to the right when you reach the far left.
this.setScrollPos(this.scrollWidth - this.clonesWidth);
this.disableScroll = true;
}
if (this.disableScroll) {
// Disable scroll-jumping for a short time to avoid flickering.
window.setTimeout(function () {
this.disableScroll = false;
}, 40);
}
}
}
}
You need to move your code from the constructor to AfterViewInit, where DOM elements are available. As a further recommendation, I would recommend that keep what you can out of the constructor. Constructor is mainly used for initializing variables, not doing any logic.
Furthermore, you have issues with this, this doesn't point to what you think it does. Take a read: How to access the correct `this` inside a callback? Very useful reading! So I would recommend to use arrow functions instead of function to keep the context of this.
So change things like:
this.context.addEventListener('scroll', function () {
to:
this.context.addEventListener('scroll', () => {
Here's a fork of your StackBlitz
PS: where you can, make use of Angular tools instead of accessing the DOM like .getElementById. Just as a future hint. Many times angular has own set of tools, and accessing and manipulating the DOM from the component should be a last resort.

Null ref after switching route in React

I have an interesting bug I can't seem to work out and I hope someone with better React knowledge than me can help me work out.
Basically, I have a component (slider carousel, like a Netflix queue) that is trying to set the visibility of two elements (nav slider buttons for left and right nav) if there is overflow of the underlying dev and/or if the underlying div is at a certain position. My visibility setter method is called when onComponentDidMount, when the position of the underlying div changes, and with an window resize event listener.
It works like expected most of the time, however, I have an edge case where I can resize the window, even after going to a new route, and it will work as expected... BUT if I go a new route again I get an error when resizing the window at that point.
It appear as if the refs are not being set after switching routes the second time because they return null.
I've tried detecting if ref is null, but couldn't get that work properly.
setCaretVis() {
const el = this.tray.current;
console.log(el);
const parent = this.wrapper.current;
console.log(parent);
const posRight = this.offsetRight();
const posLeft = el.scrollLeft;
const left = this.caretLeft.current;
const right = this.caretRight.current;
const parWidth = el.parentElement.offsetWidth;
const width = el.scrollWidth;
if (parWidth >= width) {
if (!left.classList.contains("invis")) {
left.classList.add("invis");
} else if (left.classList.contains("invis")) {
}
if (!right.classList.contains("invis")) {
right.classList.add("invis");
}
} else if (parWidth < width) {
if (left.classList.contains("invis") && posLeft != 0) {
left.classList.remove("invis");
} else if (!left.classList.contains("invis") && posLeft === 0) {
left.classList.add("invis");
}
if (right.classList.contains("invis") && posRight != 0) {
right.classList.remove("invis");
} else if (!right.classList.contains("invis") && posRight === 0) {
right.classList.add("invis");
}
}
if (posLeft > 0) {
left.classList.remove("invis");
} else {
left.classList.add("invis");
}
if (posRight === 0) {
console.log("true");
right.classList.add("invis");
} else {
right.classList.remove("invis");
}
}
offsetRight() {
const el = this.tray.current;
//const element = this.refs.tray;
const parent = this.wrapper.current;
const parWidth = parent.offsetWidth;
const width = el.scrollWidth;
const left = el.scrollLeft;
let sub = width - parWidth;
let calc = Math.abs(left - sub);
return calc;
};
// The componentDidMount method
componentDidMount() {
this.setCaretVis();
window.addEventListener("resize", this.setCaretVis);
this.setCaretVis();
}
I would like to set the visibility (adding/removing a css class) on resize after route change without error.
current error reads: Uncaught TypeError: Cannot read property 'offsetWidth' of null
I suspect that your component is recreated when you go to a new route again, but the old listener is still invoked by the resize handler. Try to remove event listener in componentWillUnmount:
componentDidMount() {
this.setCaretVis();
window.addEventListener("resize", this.setCaretVis);
this.setCaretVis();
}
componentWillUnmount() {
window.removeEventListener("resize", this.setCaretVis);
}
When router recreates the component, it will subscribe to resize event again.
From the docs:
componentWillUnmount() is invoked immediately before a component is unmounted and destroyed. Perform any necessary cleanup in this method, such as invalidating timers, canceling network requests, or cleaning up any DOM elements that were created in componentDidMount

Notifications - Alternate to Object.observe?

Trying to build a small component that sends you notifications via the Notifications Object on a small web app.
So, once a private message has been received in a group, if the value of the field has incremented or changed, then display a notification.
This works fine if you refresh the page but it is not running asynchronously.
With Object.observe() being deprecated, would somebody please explain how I would implement this? I don't quite understand how to do so with Proxies.
Thanks a lot!!
shortened for brevity
var myGroup = 0;
var notificationCount = [];
notificationCount.push({'myGroup': myGroup});
localStorage.setItem('notificationCount', JSON.stringify(notificationCount));
var storedCounts = JSON.parse(localStorage.getItem("notificationCount");
setTimeout(function() {
var newCountMyGroup = parseInt($('.myGroup .wrapper').text());
if(newCountMyGroup > 0 && storedCounts[0].myGroup !== newCountMyGroup) {
notify('New Post in My Groups', 'linkHere')
}
}, 800);
function notify(alertMessage, alertLink) {
if(Notification.permission !== "granted") {
Notification.requestPermission();
} else {
var notificationMyGroup = new Notification(...);
notificationMyGroup.onclick = function(){
window.open(alertLink);
}
}
}
From your comment:
I want a Notification to display when the div's value changes due to a message. The setTimeout function isn't running properly. The desired outcome is for it to run async as soon as a message is received. This currently only works once you refresh the page.
I read past it three times, but your current code schedules a single timed callback on page load for 800ms later. setTimeout just sets a one-off timer. If you want that to be repeated, you use setInterval instead.
So if you want to do this with polling, setInterval instead of setTimeout will do it.
If you want to do it without polling, you don't need Object.observe or Proxy because what you want to observe is a DOM element (the div), not a JavaScript object. Fortunately, there's a tool for that: Mutation observers. You could watch for childList changes to all .myGroup elements, which would tell you when a new .wrapper was added, and/or watch for characterData/childList notifications on the .wrapper elements, which will tell you when their text is changed.
Here's a quick example of both, see comments:
var wrapperId = 0;
// Our function for when a .myGroup's child list changes
function myGroupModificationCallback(records) {
// (Your real code would go here)
console.log("Saw a modification to " + records[0].target.id);
// If you want to watch wrappers, you'd set them up by calling hookUpWrapperObservers
hookUpWrapperObservers();
}
// Hook up obervers to any `.myGroup` elements that don't have them yet
function hookUpMyGroupObservers() {
$(".myGroup").each(function() {
var group = $(this);
var ob = group.data("ob");
if (!ob) {
ob = new MutationObserver(myGroupModificationCallback);
ob.observe(this, {
childList: true
});
group.data("ob", ob);
}
});
}
// Our function for when a .wrapper's character data changes
function wrapperNotificationCallback(records) {
// (Your real code would go here. Note you may get multiple records for the same wrapper.)
var changes = Object.create(null);
records.forEach(function(record) {
changes[record.target.id] = record.target;
});
Object.keys(changes).forEach(function(id) {
console.log(id + " changed: " + $(changes[id]).text());
});
}
// Hook up observers to any .wrapper elements that don't have them yet
function hookUpWrapperObservers() {
$(".myGroup .wrapper").each(function() {
var wrapper = $(this);
var ob = wrapper.data("ob");
if (!ob) {
var ob = new MutationObserver(wrapperNotificationCallback);
ob.observe(this, {
characterData: true,
childList: true
});
wrapper.data("ob", ob);
console.log(this.id + " received: " + $(this).text());
}
});
}
// Initial setup
hookUpMyGroupObservers();
hookUpWrapperObservers();
// Testing/demo: Add two wrappers to the first group and one to the
// second Update all three of them three times, then stop
setTimeout(function() {
addWrapper("#group1");
setTimeout(function() {
addWrapper("#group2");
setTimeout(function() {
addWrapper("#group1");
}, 300);
}, 300);
function addWrapper(selector) {
var wrapper = $("<div class=wrapper>1</div>");
wrapper[0].id = "wrapper" + (++wrapperId);
$(selector).append(wrapper);
var counter = 0;
var timer = setInterval(function() {
wrapper.text(parseInt(wrapper.text()) + 1);
if (++counter == 3) {
clearInterval(timer);
}
}, 300);
}
}, 300);
<div class="myGroup" id="group1"></div>
<div class="myGroup" id="group2"></div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Mutation observer support is good in modern browsers. IE9 and IE10 implemented the previous mutation events, and so there are polyfills that use events to provide a subset of observer behavior on those browers. For IE8 and earlier, you'll need to poll.
Here's a dead simple demo of using a proxy.
let o = {a: 1, b: 2};
let p = new Proxy(o, {
get: (target, name) => {
console.log(`getting ${name} on ${target}: ${target[name]}`)
return target[name];
},
set: (target, name, value) => {
console.log(`setting ${name} to ${value} on ${target}`)
target[name] = value;
}
});
p.a; // getting a on [object Object]: 1
p.a = 5; // setting a to 5 on [object Object]
p.a; // getting a on [object Object]: 5
Instead of doing a console.log, connect this to whatever event handler you'd like.
You could do something like ...
set: (target, name, value) => {
event.emit(`set:${name}`, target, value, target[name]);
target[name] = value;
}
... or whatever works for you.

Sencha: Cannot call method 'getHeader' of undefined

I am try to update the record of a dataview.List from with the data modified from it's associate form.Panel
code:
onListItemTap:function(list,index,target,record,e){
this.getMain().push({
xtype:'userform',
title:record.data.name,
record:record,
listeners:{
hide:function(form){
record.setData(form.getValues());
list.refresh();
}
}
});
},
Code after record.setData(...);list.refresh();
page getting error:
Uncaught TypeError: Cannot call method 'getHeader' of undefined
It seems like there are issue with the sencha touch 2.3.1 that I am using.
I simply comment the grouped=true property in List component. It works.
code:
{
xtype:'list',
id:'list-user',
store:'Users',
itemTpl: new Ext.XTemplate(...),
flex:1,
//grouped:true
}
hope this finding will save time and from frustration for you.
The problem is the method updateHeaderMap List component.
Ext.define('Nerine.override.List', {
override:'Ext.dataview.List',
updateHeaderMap: function() {
var me = this,
headerMap = me.headerMap,
headerIndices = me.headerIndices,
header, i, item;
headerMap.length = 0;
for (i in headerIndices) {
if (headerIndices.hasOwnProperty(i)) {
/* fix bug */
// console.log('aaa', me.getItemAt(i)); return null
// console.log('bbb', me.getItemAt(i).getHeader());
if( !(item = me.getItemAt(i))) {
continue;
}
header = me.getItemAt(i).getHeader();
headerMap.push(header.renderElement.dom.offsetTop);
}
}
}
});
The group headers have no records in the store. This override solves the issue by checking whether the record for which scrollToRecord is called really is in the store. Furthermore it solves the issue that when calling scrollToRecord is done in painted event, it is possible that the headerMap is not yet ready, which means that the pinnedHeader is not correctly updated when using scrollToRecord.
Ext.define("MyApp.override.List", {
override:'Ext.dataview.List',
scrollToRecord: function(record, animate, overscroll) {
var me = this,
store = me.getStore(),
index = store.indexOf(record);
if(index>-1 && index < this.listItems.length) {
this.updateHeaderMap();
this.callOverridden(arguments);
}
}
});

Categories