Waiting for the DOM to update in vue - javascript

In order to get an animation to work, I am trying to update data in a method consecutively, but the updates are happening to fast.
The element in question was previously animated by setting a custom property --int-menu-height to a height, retrieved via a $ref. A transitionend event is then setting that variable to auto. Now to get the same transition into the other direction, I need to first remove the auto and replace it with an interpolatable value, and then set it to zero, so that it animates between those two values. There is another transitionend event waiting, to finish the entire interaction. This is what my code for the closing looks like, right now:
const comp = this;
const menu = this.$refs.menu;
const menuHeight = this.$refs.menuHeight.clientHeight+'px';
// set it too the actual height (from previously 'auto')
this.menuStyles = { '--int-menu-height': menuHeight };
this.$nextTick( () => {
// set it to zero, so that it animates
this.menuStyles = { '--int-menu-height': 0 }
});
// never firing, because no transition
menu.addEventListener('transitionend', function closeMenu() {
comp.isMenuOpen = false;
menu.removeEventListener('transitionend', closeMenu);
});
I'm thought that $nextTick() would do the trick, but it is still happening to fast. How can I update this.menuStyles only after making sure that the previous update has fully rendered through?

Like Radeanu pointed out, I can just use setTimeout(). Because $nextTick() fires after an update in the virtual DOM, not in the real DOM. The code that I use now, that works, looks like this:
setTimeout( () => {
// set it to zero, so that it animates
this.menuStyles = { '--int-menu-height': 0 }
}, 1);

Related

Animating brooms

In Javascript, when I click on the broom, it changes the image src to a gif and changes the ID to "walkingBroom". This works.
Then, when I click on the broom, it changes the class (this makes it walks across the screen). This does not work.
I don't know what I doing wrong - can anyone help? It's like the ID has not been changed.
document.getElementById("stillBroom").addEventListener("dblclick", animate);
function animate(){
document.getElementById("stillBroom").src = "broom.gif";
document.getElementById("stillBroom").id = "walkingBroom";}
document.getElementById("walkingBroom").addEventListener("click", walk);
function walk(){
document.getElementById("walkingBroom").className = "broom pattern0 speed1";}
You should manage the logic in a variable, like let currentState="still", then on click, change to currentState="walking", and act in consequence. Currently, you are immediately trying to attach a click event to a "walkingBroom" element that doesn't exist (it will exist, but later, after you click the broom once). For that matter, your console is very likely saying Error - Can't read property addEventListener of null and your script crashes without running. Open your console, it's very useful.
let currentState = "still";
broom.addEventListener("dblclick", clickHandler);
const clickHandler = () => {
switch(currentState) {
case "still" :
currentState="walking";
broom.src="broom.gif";
break;
case "walking":
currentState="flying";
broom.className = "broom pattern0 speed1";
break;
}
}

How to make sure that all events are unattached when called multiple times in Javascript?

I'm using dragswipe, a plugin from here. It works perfectly fine. However, I have a requirement to do dynamic carousels. So, every time a user changes something on a page, the carousels get updated dynamically. I thought I could just recall the plugin to update the element, but somehow when I dragged the carousel the functional gets called multiple times.
For example, I do this to initialise the plugin.
function init() {
$('#carousel').dragswipe({
width: 320,
current_page_element: '#current_page',
total_pages_element:'#total_pages'
});
}
So after the page is updated via ajax I have a callback to call this method again like this
function callback() {
init();
}
Everything should is updated perfectly fine, but when I started dragging. The carousel skips some pages. I thought I had to unbind all the events so I tried this. $('#carousel').unbind() but the problem still persists.
Here's the source code when the plugin is initialised.
$.fn.dragswipe = function(options) {
options = $.extend({
offset: 0,
turn_threshold: 0.1,
current_page: 0
},options)
this.each(function() {
option = $(this).hammer({
drag_vertical: false,
swipe_time: 20
});
// start a drag.
// if we're moving left or right, set our initial distance to 0.
$(this).bind('dragstart',function(ev) {
console.log('dragstart');
if (ev.direction=='left' || ev.direction=='right') {
drag_distance = 0;
}
});
// while dragging, change the dragging distance based on how far a user has dragged.
$(this).bind('drag',function(ev) {
console.log('drag');
if (ev.direction=='left' || ev.direction=='right') {
drag_distance = ev.distanceX;
var options = CONFIGS[$(this).data('dragswipe_id')];
$(this).updateOffset(options.offset+drag_distance);
}
});
$(this).bind('dragend',function(ev) {
console.log('dragend');
if (ev.direction=='left' || ev.direction=='right') {
var options = CONFIGS[$(this).data('dragswipe_id')];
if (Math.abs(drag_distance / options.width) > options.turn_threshold) {
if (ev.direction=='left') {
options.current_page++;
}
if (ev.direction=='right') {
options.current_page--;
}
}
// store modified options
CONFIGS[$(this).data('dragswipe_id')] = options;
console.log(options.current_page);
$(this).gotoPage(options.current_page,true);
}
});
// set the dragswipe ID used to look up config options later.
$(this).data('dragswipe_id',CONFIGS.length);
// store config options.
CONFIGS[$(this).data('dragswipe_id')] = options;
});
}
Which I see nothing wrong with the plugin, but maybe I'm missing something obvious.
UPDATED
I have created the example in jsfiddle, but it's not working just in case anyone can fix the problem. Also, on the plugin site itself. The problem can be reproduce by running this code to initialise the plugin multiple times. After running the code twice, when you drag the page it goes to the last page instead of the second page.
$('#carousel').dragswipe({
width: 320,
current_page_element: '#current_page',
total_pages_element:'#total_pages'
});
Have you tried turning it off and on again?
Dragswipe has a removeDragswipe() method that does a little bit more than just unbinding, and it also explicitly only unbinds events that are related to dragswipe itself so it won't mess up any other events that may have been bound.
So instead of unbind(), do this:
function callback() {
$('#carousel').removeDragswipe();
init();
}
As discussed in the comments, this doesn't work because it seems that even after unbinding all the events, after re-binding them by activating the plugin again the events seem to fire an extra time.
What does seem to be a workaround is to actually rebuild the entire element so that no events can linger:
function callback() {
$('#carousel').removeDragswipe();
rebuildCarousel();
init();
}
function rebuildCarousel() {
var wrapper = $('#carousel').parent;
var html = parent.html();
$('#carousel').remove();
wrapper.html(html);
}

SnapSVG animation callback on set of elements not firing

See JSfiddle!
I am wanting to animate a set of elements and execute a callback when finished like so:
s.selectAll('.active').animate( {
transform: matrix
},
300,
mina.linear,
function() {
//callback doesnt fire
alert('callback')
}
)
The elements are animated correctly but the callback isnt executed.
However, when I apply the animation to a group of elements, the callback is fired:
group.animate( {
transform: matrix
},
300,
mina.linear,
function() {
alert('callback')
}
)
.. But I don't want to put my selected elements in a group as this would cause more complications in other places.
Is it possible to animate a set of elements that I got via a .select() or .selectAll() while being able to fire the callback?
Thanks a lot in advance!
Edit: For future readers, you can animate a set of elements by using forEach and counting if all elements are done animating:
function hideToPoint(elements, x, y, callback) {
var finished = 0;
elements.forEach(function(e) {
e.animate( {
//do stuff
},
300,
mina.linear,
function () {
finished++;
if (finished == elements.length) {
callback();
}
}
)
})
}
I'm going to have a stab at answering a couple of problems, even though I'm not sure if related to the callback. Its hard to tell if its just the example code or not without a proper test like a jsfiddle.
However, there are at least 2 problems in the code above.
Creating a matrix is with
new Snap.Matrix(); // as opposed to Snap.matrix()
Also
elements.animate()
The problem here is that animate acts on one element (edit: looks like it can work on elements within a set, but not the callback as example here, edit2: callbacks on sets may now be supported), not multiple elements as such (you can sometimes apply somethings to a set which deals with them individually, but as far as I'm aware, thats not the case with animate).
So you either want to do a
elements.forEach( function(el) { el.animate({blah: value}, 2000, mina.linear.callback )
});
or if its an svg group (as opposed to a set), the original code would possibly work (but I would call it 'myGroup' or something instead of 'elements' for code readability and guessing what it contains)
fiddle (have included a different animation using snap animation string)

Why is this onClick event not being applied correctly?

I'm using late-binding to assign onClick events to checkboxes (styled as dots). My first assignment [located at https://github.com/farfromunique/vampirrePoints/blob/master/events.js#L264 ] goes perfectly, and reacts as expected - all dots have this as their onClick:
/* https://github.com/farfromunique/vampirrePoints/blob/master/main.js#L440 */
allDots[i].onclick = function() {
if (this.checked) {
decrementCounter(1);
} else {
incrementCounter(1);
};
}
However, when Step11() is triggered, and freebieDotSetup() is called, only some of the checkboxes get their onClicks updated. Specifically, sta1, sta2, sta3, sta4, sta5 (possibly others, too) keep their initial value.
I have tried putting console.log() statements in during the assignment process, and it looks like the assignment happens, but it doesn't "stick". Why doesn't this work?
Code reference (whole site): https://github.com/farfromunique/vampirrePoints
By request, a non-working JSFiddle: http://jsfiddle.net/farfromunique/mS4Lp/
Note: the menu is on the wrong side, doesn't advance properly, and none of the "dots" (checkboxes) are clickable, so functionality is not testable. I strongly suspect that my code is not cross-browser compatible, but that isn't a priority for me (yet).
The solution: In the function freebieDotSetup(), I was doing this:
for (i=0;i<attributeDots.length;i++) {
attributeDots[i] = allDots[i];
}
...
for (i=0;i<abilityDots.length;i++) {
abilityDots[i] = allDots[i+attributeDots.length];
}
...
for (i=0;i<disciplineDots.length;i++) {
disciplineDots[i] = allDots[i+abilityDots.length];
}
...
Since I was using the previous group's length, it would occasionally overwrite the previous values (due to groups being shorter). I corrected this with this:
startPos = startPos + attributeDots.length;
for (i=0;i<abilityDots.length;i++) {
abilityDots[i] = allDots[i+startPos];
}
Adding the previous group's length to startPos each time. Making this change resolved my issue.
... But the JSFiddle is totally busted, still.

Javascript while loop changing scroll position of div crashes site

I have a div displaying some horizontally scrollable images with white-space:nowrap; overflow-x:scroll and i'm trying to make the function below work:
var mouseIsInDiv = false;
function autoScroll() {
var i = 1;
while (mouseIsInDiv = false) {
setTimeout(function(){
document.getElementById("theDiv").scrollLeft = i;
i++;
},50);
}
}
It is supposed to loop through (while the mouse is not within this scrollable div) incrementing the scroll position by 1px every 50 miliseconds. In other words it's supposed to scroll through the images automatically when this function is called. I'm not getting any syntactic errors but whenever i press a button that calls this function on a webpage, the browser crashes completely - I'm using the latest versions of Chrome, Safari and Firefox. Any ideas would be really helpful, I've been tearing my hair out over this!
Your loop creates many timeouts that happens in the same time (after 50 milisecs) you need to set the timeout recursivly, inside the set timeout function, and ask if mouseISInDiv inside the set timeout function as well.
The current code state, the loop will run many many times in a small amount of time, and page will crush(it's liek infinite) and after 50 millisecs there will be many set timeouts that ran.
I had a fun time working on this one, so I'll post my response despite the correct answer already having been accepted.
Basically, you need to restructure everything so that the whole scheme is asynchronous. That means event listeners respond to mouse movement, and there are no while loops.
Thus, I present this fiddle. Here is the javascript:
var mouseIsInDiv = false;
var theDiv = document.getElementById("theDiv");
theDiv.onmouseover = function() { mouseIsInDiv = true; };
theDiv.onmouseout = function() {
mouseIsInDiv = false;
scrollLeft1();
};
function scrollLeft1() {
if (mouseIsInDiv == false && theDiv.scrollLeft < theDiv.clientWidth) {
theDiv.scrollLeft += 1;
setTimeout(scrollLeft1, 50);
}
}
scrollLeft1();
As you can see, the function calls itself recursively and asynchronously, and the whole thing can be restarted after manually resetting the scroll. You could also add an event listener for the scroll completion.

Categories

Resources