Related
Code :-
<template>
// html
</template>
<script>
import _ from "lodash";
data() {
return {
renderComponent: false,
};
},
watch: {
// when this property is true, want to stop calling scroll event with this.onScroll method
renderComponent(val) {
if(val === true) {
console.log("////////I am removing you");
window.removeEventListener('scroll', this.onScroll);
}
}
},
methods: {
onScroll() {
console.log("I am called////////");
let similarTickerHeading = this.$refs.similarTicker;
if(similarTickerHeading) {
let margin = similarTickerHeading.getBoundingClientRect().top;
let innerHeigth = window.innerHeight;
console.log("Window Screen", innerHeigth);
console.log("Component located", margin);
// when this condition is true, I want to stop listening for the scroll event with this (onScroll method)
if(margin - innerHeigth < 850) {
console.log("I should start loading the actual component");
this.renderComponent = true;
this.$vs.loading.close("#loader-example > .con-vs-loading");
// removing eventListener for scrolling with the onScroll Method
window.removeEventListener('scroll', this.onScroll);
}
}
}
},
mounted() {
this.renderComponent = false;
this.$vs.loading({
container: "#loader-example",
type: "point",
scale: 0.8,
});
this.$nextTick(function() {
window.addEventListener('scroll', _.throttle(this.onScroll,250));
this.onScroll();
})
},
beforeDestroy() {
window.removeEventListener('scroll', this.onScroll);
},
</script>
In the above code, I want to stop listening for the scroll event with onScroll method when my if block in onScroll method becomes true. But, still, the onScroll method gets called whenever I scroll even though when I tried to remove the eventListener. I even created a watcher to remove the eventListener, yet the method keeps on getting called on scroll.
How can I remove the scroll eventListener with onScroll method ?
UPDATE : If I remove throttling and cut out _.throttle, the scroll event does get removed. Due to the use of _.throttle, I cannot remove the scroll event listener.
The function reference passed to window.addEventListener() must be the same reference passed to window.removeEventListener(). In your case, there are two different references because you've wrapped one of them with _.throttle().
Solution
Cache the function reference passed to addEventListener() so that it could be used later for removeEventListener():
export default {
mounted() {
๐
this._scrollHandler = _.throttle(this.onScroll, 250)
this.$nextTick(() => { ๐
window.addEventListener('scroll', this._scrollHandler);
this.onScroll();
})
},
beforeDestroy() {
๐
window.removeEventListener('scroll', this._scrollHandler);
},
}
demo
I am working on a webapp with drag 'n drop functionality.
Up to now I used basic HTML5 Dragging but now I switched to using the library interact.js.
I don't know if my problem is specific to this library or of more general kind:
Events when dragging and dropping usually fire multiple times (if I have watched it correctly, it also seems to always be exactly 4 times, but no guarantee on that).
I am also using Vue.js and this is my code:
<template>
<v-card
elevation="0"
:id="id"
class="board device-dropzone"
>
<slot class="row"/>
<div
Drop Card here
</div>
</v-card>
</template>
In the slot, an image and div with text get added. Also this is the script:
<script>
import interact from 'interactjs';
export default {
name: 'Devices',
props: ['id', 'acceptsDrop'],
data() {
return {
extendedHover: false,
hoverEnter: false,
timer: null,
totalTime: 2,
};
},
methods: {
resetHover() {
alert('reset');
},
drop(e) {
let wantedId = e.relatedTarget.id.split('-')[0];
console.log(wantedId);
console.warn(e.target);
e.target.classList.remove('hover-drag-over');
this.extendedHover = false;
console.log('-------------dropped');
console.warn('dropped onto device');
this.$emit('dropped-component', cardId);
e.target.classList.remove('hover-drag-over'); */
},
dragenter() {
console.log('------------dragenter');
this.hoverEnter = true;
setTimeout(() => {
this.extendedHover = true;
console.log('extended hover detected');
}, 2000);
} */
this.timerID = setTimeout(this.countdown, 3000);
},
dragover(e) {
if (this.acceptsDrop) {
e.target.classList.add('hover-drag-over');
}
},
dragleave(e) {
if (this.acceptsDrop) {
clearInterval(this.timer);
this.timer = null;
this.totalTime = 2;
e.target.classList.remove('hover-drag-over');
this.extendedHover = false;
this.hoverEnter = false;
console.log('................');
console.warn(this.extendedHover);
// this.$emit('cancel-hover');
}
},
countdown() {
console.log('!!!!!!!!!!!!!!!!!!!!');
if (this.hoverEnter === true) {
this.extendedHover = true;
console.log('-------------------');
console.warn(this);
console.warn(this.extendedHover);
this.$emit('long-hover');
}
},
},
mounted() {
// enable draggables to be dropped into this
const dropzone = this;
interact('.device-dropzone').dropzone({
overlap: 0.9,
ondragenter: dropzone.dragenter(),
ondrop: function (event) {
dropzone.drop(event);
},
})
},
};
</script>
The draggable component is this one:
<template>
<v-card
class="primary draggable-card"
:id = "id"
:draggable = "false"
#dragover.stop
ref="interactElement"
>
<slot/>
</v-card>
With the script:
<script>
import interact from 'interactjs';
export default {
props: ['id', 'draggable'],
data() {
return {
isInteractAnimating: true,
position: { x: 0, y: 0 },
};
},
methods: {
/* dragStart: (e) => {
e.stopPropagation(); // so dragStart of ParentBoard does not get triggered as well
// eslint-disable-next-line
const target = e.target;
e.dataTransfer.setData('card_id', target.id);
e.dataTransfer.setData('type', 'widget');
// for some delay
setTimeout(() => {
console.log('started dragging');
}, 0);
}, */
dragEndListener: (event) => {
console.warn('+++++++++++++++++++++++++++');
// console.warn(event.currentTarget.id);
if (document.getElementById(event.currentTarget.id)) {
event.currentTarget.parentNode.removeChild(event.currentTarget);
}
},
dragMoveListener: (event) => {
/* eslint-disable */
var target = event.target;
// keep the dragged position in the data-x/data-y attributes
const xCurrent = parseFloat(target.getAttribute('data-x')) || 0;
const yCurrent = parseFloat(target.getAttribute('data-y')) || 0;
const valX = xCurrent + event.dx;
const valY = yCurrent + event.dy;
// translate the element
event.target.style.transform =
`translate(${valX}px, ${valY}px)`
// update the postion attributes
target.setAttribute('data-x', x);
target.setAttribute('data-y', y);
}
/* eslint-enable */
},
mounted() {
const element = this.$refs.interactElement;
console.log(element);
// interact(element).draggable({
const component = this;
interact('.draggable-card')
.draggable({
manualStart: true,
onmove: component.dragMoveListener,
onend:component.dragEndListener,
})
.on('move', function (event) {
var interaction = event.interaction;
// if the pointer was moved while being held down
// and an interaction hasn't started yet
if (interaction.pointerIsDown && !interaction.interacting()) {
var original = event.currentTarget;
// create a clone of the currentTarget element
const clone = event.currentTarget.cloneNode(true);
clone.id = clone.id + "-clone";
clone.classname += " dragged-clone";
// insert the clone to the page
document.body.appendChild(clone);
clone.style.opacity = 0.5;
// start a drag interaction targeting the clone
interaction.start({ name: 'drag' },
event.interactable,
clone);
}
})
.on('end', function (event) {
console.error('end drag');
});
},
/* eslint-enable */
};
</script>
In general the dragging and dropping works.
But I don't get why e.g. the drop-event would trigger four times when only dropping a single card.
Can anybody help me with this?
I was facing a similar issue using the same framework and library. Since there was some logic based on the event firing, it was breaking my app when it fired multiple times.
I suspected that the issue was related to bubbling of events.
The solution in my case was therefore to add event.stopImmediatePropagation() inside my drop(event) handler.
As noted in the referenced article, one should take care when stopping event bubbling that it doesn't have unforeseen consequences elsewhere.
I disable mousewheel with javascript, when i scroll the page to the special block.
I use a lot of methods, but all of them worked in firefox, not in chrome.
For chrome i find a specital method with preventDefault.
But i don't know how to enable them.
This is the code:
const options = {
rootMargin: "-400px"
};
const observer = new IntersectionObserver(function (entries, observer) {
entries.forEach(entry => {
console.log(entry.isIntersecting);
if (entry.isIntersecting === true) {
document.querySelector('body').classList.add('fixed');
document.body.addEventListener('wheel', function (e) {
e.preventDefault();
}, {passive: false});
$('html, body').animate({
scrollTop: scrollCenter
}, 1000);
setTimeout(function () {
document.querySelector('body').classList.remove('fixed');
document.body.addEventListener('wheel', function (e) {
// override prevented flag to prevent jquery from discarding event
e.isDefaultPrevented = function () {
return false;
}
});
}, 1000);
}
});
}, options);
observer.observe(produtsBlock);
Thanks for help.
Stacking up a listener to invalidate a previous listener is tricky as they are fired in FIFO sequence.
Some options:
1. Removing the listener
You could remove the blocking listener after that period of time.
With a named function:
const prevent = (e) => e.preventDefault();
document.body.addEventListener('wheel', prevent, { passive: false });
You can remove it afterwards:
setTimeout(function () {
document.querySelector('body').classList.remove('fixed');
document.body.removeEventListener('wheel', prevent);
}, 1000);
2. Using a flag
Use a state flag to handle preventDefault.
const state = { prevent: true };
const prevent = (e) => {
if (state.prevent) {
e.preventDefault();
}
};
document.body.addEventListener("wheel", prevent, { passive: false });
Then alter the flag:
setTimeout(function () {
document.querySelector("body").classList.remove("fixed");
state.prevent = false;
}, 1000);
The documentation has both Events and EventListeners. I can get the EventListeners to fire but the Events do not have adequate documentation for me to get going. What is the difference and how do you use? Thank you.
https://github.com/airbnb/lottie-web#events
Events (Do not work, how to use?)
// From the Documentation
onComplete
onLoopComplete
onEnterFrame
onSegmentStart
you can also use addEventListener with the following events:
complete
loopComplete
enterFrame
segmentStart
config_ready (when initial config is done)
data_ready (when all parts of the animation have been loaded)
data_failed (when part of the animation can not be loaded)
loaded_images (when all image loads have either succeeded or errored)
DOMLoaded (when elements have been added to the DOM)
destroy
// End Documentation
From the standard addEventListener usage, this works...
birbSequence.addEventListener('loopComplete', (e) => {
console.log(e);
});
although 'complete' does not fire.
But to try out the stuff in Events like onEnterFrame?
var birbSequence = lottie.loadAnimation({
container: bodyMovinContainer1,
loop: true,
renderer: 'svg',
path: 'Birb Sequence 1.json',
onEnterFrame: function(e) { console.log(e); }
});
I am really new to using Lottie though so could use some help.
Just want a way to see how to use Events
Let's say we have our lottie animation:
const anim = lottie.loadAnimation({
container: '#container',
renderer: 'svg',
loop: true,
autoplay: true,
...
})
With Events:
anim.onComplete = function() {
console.log('complete')
}
anim.onLoopComplete = function() {
console.log('loopComplete')
}
With addEventListener:
anim.addEventListener('complete', function() {
console.log('complete')
})
anim.addEventListener('loopComplete', function() {
console.log('loopComplete')
})
You can use the addEventListener method to listen to all the events instead of the on* series of event hooks.
const options = {
container: '#container',
loop: false,
autoplay: false,
renderer: 'svg',
rendererSettings: {
scaleMode: 'noScale',
clearCanvas: false,
progressiveLoad: true,
hideOnTransparent: true,
},
};
try {
const anim = lottie.loadAnimation({ ...options, path: 'URL_TO_JSON' });
anim.addEventListener('complete', () => { console.log('complete'); });
anim.addEventListener('loopComplete', () => { console.log('loopComplete'); });
anim.addEventListener('data_ready ', () => { console.log('data_ready'); });
anim.addEventListener('data_failed', () => { console.log('data_failed'); });
anim.addEventListener('enterFrame', () => {
console.log('enterFrame', anim.currentFrame);
});
// etc ...
} catch (error)
console.log('error loading anim');
}
Hope that helps!
I have a vue component for video upload, where I am warning a user when he tries to navigate away during the video upload that he will lose the file if he does so, like this:
ready() {
window.onbeforeunload = () => {
if (this.uploading && !this.uploadingComplete && !this.failed) {
this.confirm('Are you sure you want to navigate away? Your video won't be uploaded if you do so!');
}
}
}
I am using sweetalert to alert the user about it. But how can I then make it stay on the same page, and prevent the navigation away before he confirms that he wants to navigate away?
This is the whole component:
<script>
function initialState (){
return {
uid: null,
uploading: false,
uploadingComplete: false,
failed: false,
title: null,
link: null,
description: null,
visibility: 'private',
saveStatus: null,
fileProgress: 0
}
}
export default {
data: function (){
return initialState();
},
methods: {
fileInputChange() {
this.uploading = true;
this.failed = false;
this.file = document.getElementById('video').files[0];
this.store().then(() => {
var form = new FormData();
form.append('video', this.file);
form.append('uid', this.uid);
this.$http.post('/upload', form, {
progress: (e) => {
if (e.lengthComputable) {
this.updateProgress(e)
}
}
}).then(() => {
this.uploadingComplete = true
}, () => {
this.failed = true
});
}, () => {
this.failed = true
})
},
store() {
return this.$http.post('/videos', {
title: this.title,
description: this.description,
visibility: this.visibility,
extension: this.file.name.split('.').pop()
}).then((response) => {
this.uid = response.json().data.uid;
});
},
update() {
this.saveStatus = 'Saving changes.';
return this.$http.put('/videos/' + this.uid, {
link: this.link,
title: this.title,
description: this.description,
visibility: this.visibility
}).then((response) => {
this.saveStatus = 'Changes saved.';
setTimeout(() => {
this.saveStatus = null
}, 3000)
}, () => {
this.saveStatus = 'Failed to save changes.';
});
},
updateProgress(e) {
e.percent = (e.loaded / e.total) * 100;
this.fileProgress = e.percent;
},
confirm(message) {
swal({
title: message,
text: null,
type: "warning",
showCancelButton: true,
cancelButtonText: "Cancel",
cancelButtonColor: '#FFF',
confirmButtonColor: "#2E112D",
confirmButtonText: "Yes, delete"
}).then(function(){
this.$data = initialState();
}.bind(this), function(dismiss) {
// dismiss can be 'overlay', 'cancel', 'close', 'esc', 'timer'
if (dismiss === 'cancel') { // you might also handle 'close' or 'timer' if you used those
// ignore
} else {
throw dismiss;
}
})
}
},
ready() {
window.onbeforeunload = () => {
if (this.uploading && !this.uploadingComplete && !this.failed) {
this.confirm('Are you sure you want to navigate away? Your video won't be uploaded if you do so!');
}
}
}
}
</script>
Mozilla documentation suggests
window.onbeforeunload = function(e) {
var dialogText = 'Dialog text here';
e.returnValue = dialogText;
return dialogText;
};
and also states that:
Since 25 May 2011, the HTML5 specification states that calls to window.alert(), window.confirm(), and window.prompt() methods may be ignored during this event. See the HTML5 specification for more details.
Source contains many other details regarding reasons and what to expect from modern browsers.
This question seems to be a duplicate of yours.
This answer suggests that to avoid weird browser behaviour you should set handler only when it's to prevent something (that is while navigating away should trigger a confirmation dialog)
But how can I then make it stay on the same page, and prevent the navigation away before he confirms that he wants to navigate away?
Add return false; to stop the event.
if (this.uploading && !this.uploadingComplete && !this.failed) {
this.confirm("Are you sure you want to navigate away? Your video won't be uploaded if you do so!");
return false; // <==== add this
}
return false; does 3 separate things when you call it :
event.preventDefault(); โ It stops the browsers default behaviour.
event.stopPropagation(); โ It prevents the event from propagating (or โbubbling upโ) the DOM.
Stops callback execution and returns immediately when called.