Vue - watch for changes on this.$el.clientWidth - javascript

Is it possible to watch for changes on components element clientWidth (this.$el.clientWidth)?
Something like:
this.$watch(
() => {
return this.$el.clientWidth;
},
(newWidth, oldWidth) => {
console.log(newWidth);
}
)

I am investigating the same problem.
I think the solution dependent on the HTML lifecycle is effective in some cases.
I am pleased if my solution at the present stage will serve you.
mousemove (e) {
this.clientWidth = this.$el.clientWidth
}

Related

What is the proper way to smoothly scale an element to nothing and then back to its original size

This is how I do it in React:
const animate_boxes = () => {
inner_ref.current.style.transform = "scale(0)";
setTimeout(() => {
if (inner_ref && inner_ref.current) {
inner_ref.current.style.transform = "scale(1)";
}
}, 200);
};
For some reason, it is not dependable. the setTimeout may not always get run. Still haven't figured out why yet. But If there is an alternative way to do it, then please write a solution.
thanks
May I suggest the react-spring library?
Use framer motion library with scale property passed in as an array of values
eg :
import {motion} from 'framer-motion'
const Component = () => {
return (
<motion.div
initial={{scale: 1}}
animate={{scale: [0.1, 1]}}
>
hi
</motion.div>
)
}
Refer this docs for more examples - Docs

In React 17 jquery issue?

I'm using React. I need jquery functionality. I solved this problem with npm install jquery. But is such a use correct? I have never seen a user before. Will I encounter a problem?
It's quite strange to mix a framework like React with jQuery. Not completely unheard of, but it's almost never the right way to do things.
Here, you can achieve the same effect more properly by
using addEventListener instead of jQuery
using useEffect to add the listener once (and to remove it once the component unmounts)
Setting state which changes the nav's class via JSX instead of doing it with DOM manipulation
const Header = () => {
const [shrink, setShrink] = useState(false);
useEffect(() => {
const handler = () => {
setShrink(window.scrollY > 50);
}
document.addEventListener('scroll', handler);
return () => document.removeEventListener('scroll', handler);
}, []);
const navClass = (shrink ? 'shrink ' : '') + 'navbar navbar-expand-lg'; // etc
return (
<header>
<nav className={navClass}>
...

Vue JS - How to get window size whenever it changes

I'm new to vuejs but I was trying to get the window size whenever I
resize it so that i can compare it to some value for a function that I
need to apply depending on the screen size. I also tried using the
watch property but not sure how to handle it so that's probably why it didn't work
methods: {
elem() {
this.size = window.innerWidth;
return this.size;
},
mounted() {
if (this.elem < 767){ //some code }
}
Put this code inside your Vue component:
created() {
window.addEventListener("resize", this.myEventHandler);
},
destroyed() {
window.removeEventListener("resize", this.myEventHandler);
},
methods: {
myEventHandler(e) {
// your code for handling resize...
}
}
This will register your Vue method on component creation, trigger myEventHandler when the browser window is resized, and free up memory once your component is destroyed.
For Vue3, you may use the code below:
mounted() {
window.addEventListener("resize", this.myEventHandler);
},
unmounted() {
window.removeEventListener("resize", this.myEventHandler);
},
methods: {
myEventHandler(e) {
// your code for handling resize...
}
}
destroyed and beforeDestroyed is deprecated in Vue3, hence you might want to use the beforeUnmount and unmounted
Simplest approach
https://www.npmjs.com/package/vue-window-size
Preview
import Vue from 'vue';
import VueWindowSize from 'vue-window-size';
Vue.use(VueWindowSize);
You would then access it normally from your components like this:
<template>
<div>
<p>window width: {{ windowWidth }}</p>
<p>window height: {{ windowHeight }}</p>
</div>
</template>
I looked at the code of that library vue-window-size, and besides the additional logic, it's just adding an event listener on window resize, and it looks like it can be instructed to debounce. Source
The critical problem for me is that my Vue SPA app does not emit a window resize event when a vue-router route changes that makes the <html> element go from 1000px to 4000px, so it's causing me all kinds of problems watching a canvas element controlled by p5.js to redraw a wallpaper using p5.resizeCanvas().
I have a different solution now that involves actively polling the page's offset height.
The first thing to be aware of is JavaScript memory management, so to avoid memory leaks, I put setInterval in the created lifecycle method and clearInterval in the beforeDestroy lifecycle method:
created() {
this.refreshScrollableArea = setInterval(() => {
const { offsetWidth, offsetHeight } = document.getElementById('app');
this.offsetWidth = offsetWidth;
this.offsetHeight = offsetHeight;
}, 100);
},
beforeDestroy() {
return clearInterval(this.refreshScrollableArea);
},
As hinted in the above code, I also placed some initial state:
data() {
const { offsetWidth, offsetHeight } = document.querySelector('#app');
return {
offsetWidth,
offsetHeight,
refreshScrollableArea: undefined,
};
},
Note: if you are using getElementById with something like this.id (ie: an element that is a child in this component), document.getElementById(this.id) will be undefined because DOM elements load outer-to-inner, so if you see an error stemming from the data instantiation, set the width/height to 0 initially.
Then, I put a watcher on offsetHeight to listen for height changes and perform business logic:
watch: {
offsetHeight() {
console.log('offsetHeight changed', this.offsetHeight);
this.state = IS_RESET;
this.setState(this.sketch);
return this.draw(this.sketch);
},
},
Conclusion: I tested with performance.now() and:
document.querySelector('#app').offsetHeight
document.getElementById('app').offsetHeight
document.querySelector('#app').getClientBoundingRect().height
all execute in about the exact same amount of time: 0.2ms, so the above code is costing about 0.2ms every 100ms. I currently find that reasonable in my app including after I adjust for slow clients that operate an order of magnitude slower than my localmachine.
Here is the test logic for your own R&D:
const t0 = performance.now();
const { offsetWidth, offsetHeight } = document.getElementById('app');
const t1 = performance.now();
console.log('execution time:', (t1 - t0), 'ms');
Bonus: if you get any performance issue due to long-running execution time on your setInterval function, try wrapping it in a double-requestAnimationFrame:
created() {
this.refreshScrollableArea = setInterval(() => {
return requestAnimationFrame(() => requestAnimationFrame(() => {
const { offsetWidth, offsetHeight } = document.getElementById(this.id);
this.offsetWidth = offsetWidth;
this.offsetHeight = offsetHeight;
}));
}, 100);
},
requestAnimationFrame itself a person should research. I will leave it out of the scope of this answer.
In closing, another idea I researched later, but am not using is to use a recursive setTimeout function with a dynamic timeout on it (ie: a timeout that decays after the page loads); however, if you consider the recursive setTimeout technique, be conscious of callstack/function-queue length and tail call optimization. Stack size could run away on you.
You can use this anywhere anytime
methods: {
//define below method first.
winWidth: function () {
setInterval(() => {
var w = window.innerWidth;
if (w < 768) {
this.clientsTestimonialsPages = 1
} else if (w < 960) {
this.clientsTestimonialsPages = 2
} else if (w < 1200) {
this.clientsTestimonialsPages = 3
} else {
this.clientsTestimonialsPages = 4
}
}, 100);
}
},
mounted() {
//callback once mounted
this.winWidth()
}

Clear Interval doesn't work in VUE

I am trying to build a Pomodoro Timer using VUE.js, below is the relevant code snippet that I had problems on. So the problem is that the clearInterval Method doesn't seem to work here.
data: {
workTimeInMin: 1,
breakTimeInMin: 1,
timeRemainInMillisecond: 0,
timerOn: false,
rest: false
},
methods: {
startOrStopTheTimer: function() {
var self = this;
var interval;
if (this.timerOn){
this.timerOn = !this.timerOn;
window.clearInterval(interval);
console.log("clearInterval");
}else {
this.timerOn = !this.timerOn;
interval = setInterval(function(){
console.log("123");
if (self.timeRemainInMillisecond <= 0) {
console.log("1");
self.rest = !self.rest;
if (self.rest) {
self.timeRemainInMillisecond = self.restTimeInMin*60*1000;
}else {
self.timeRemainInMillisecond = self.workTimeInMin*60*1000;
}
}
this.timeRemainInMillisecond = this.timeRemainInMillisecond-1000
}, 1000);
}
},
You can find a live demo here.
In the page, when I click start an set Interval method is called. Then I clicked the stop, it is supposed to clear the interval, however it doesn't seem to work as I intend it to. You can inspect the console and easily find the "123" keeps popping up which indicates that the interval is not cleared.
After searching a while in the StackOverFlow, I found that if I define the variable interval to in the global context it will work as I intend it. But I wish to know why it is so.
Your help will be highly appreciated. Thanks in advance!
Your problem is that var interval is a declared new every time the function is called. So the variable that holds a reference to your interval is not accessible.
Just use this.interval and the variable will added to the data-section of your component under the hood.
You can also just use this mixin (plugin) / have a look how it works there:
Vue interval mixin
You can clear setInterval and clearinterval. here is sample code please modify according to your need
if (val && !this.t) {
this.t = setInterval(() => {
location.reload()
}, 10 * 1000)
} else {
clearInterval(this.t)
}
I arrived at this page because Vue still seems to handle setInterval in the strange way as described by the OP. I tried all the fixes above and found only two options work:
Global scope the interval variable (as mentioned above and less than an ideal workaround)
Use Vuex to store the timer and handle it the usual Vuex way.
I was happy to find that using Vuex bypasses the problem, since I'm using Vuex anyway. It doesn't shed light on why this issue exists, but it does give us a way to move forward with little sacrifices made.
So just declare the variable in the Vuex state, use mutations to start and stop the timer and all works as expected.
This works like a charm:
data () {
return {
polling: null
}
},
methods: {
pollData () {
this.polling = setInterval(() => {
console.log('fired...')
}, 3000)
}
},
beforeDestroy () {
clearInterval(this.polling)
},
created () {
this.pollData()
}

YUI3 custom async events not working on Y.Global

I am trying implement async event leveraging YUI3 library. So the application had been notified about event passed even with late subscription, simular like load or ready events do.
Here it is what I have so far, but no luck around.
YUI().use('event', 'event-custom', function(Y){
function onCustomEvent () {
Y.Global.on('custom:event', function(){
alert('custom fired');
});
}
window.setTimeout(onCustomEvent, 2000);
});
YUI().use('event', 'event-custom', function(Y){
Y.publish('custom:event', {
emitFacade: true,
broadcast: 2,
fireOnce: true,
async: true
});
function fireCustomEvent () {
Y.Global.fire('custom:event');
}
window.setTimeout(fireCustomEvent, 1000);
});
If anyone could give a hint what's wrong with this code? Thank you.
UPD:
After a bit investigations it turns out that async events work fine inside one use() instance and when not using Global broadcasting. So that's something either bug or limitation. Still discovering
Okay, at the high level the inconsistency with global events (how I understood it) lays in the sandbox nature of Y object. So at some point you could fire only sync events globally cause async parameters you subscribe to custom event made on Y instance and not passed further (and than YUI uses some defaults or whatever). This possibly makes sense but than why such kind of events should be fireable globally? Either I miss some substantial part of YUI and this is candidate for bug report.
Anyway I do not have time to dive deeper in YUI and what I really practically need could be wrapped in 40 lines of code:
YUI.add('async-pubsub', function(Y) {
var subscribers = {};
if ( !YUI.asyncPubSub ) {
YUI.namespace('asyncPubSub');
YUI.asyncPubSub = (function(){
var eventsFired = {};
function doPublishFor(name) {
var subscriber;
for ( subscriber in subscribers ) {
if ( subscriber === name ) {
(subscribers[name]).call();
delete ( subscribers[name] ); // Keep Planet clean
}
}
}
return {
'publish': function(name, options) {
eventsFired[name] = options || {};
doPublishFor(name);
},
'subscribe': function(name, callback) {
if ( subscribers[name] ) {
Y.log('More than one async subscriber per instance, overriding it.', 'warning', 'async-pubsub');
}
subscribers[name] = callback || function() {};
if ( eventsFired[name] ) {
window.setTimeout(
function () {
doPublishFor(name);
},0
);
}
}
};
})();
}
Y.asyncPubSub = YUI.asyncPubSub;
}, '1.0', {requires: []});
There is some limitation and room for optimization here, like ability subscribe only one action for one event per use instance, but I do not need more. I will also try to debug and enhance this snippet in future if there will be interest.
Still curious about YUI behavior, is it bug or something?

Categories

Resources