GreenSock JS adding dom element within Tween onComplete() - dom

I'm trying to get past a GreenSock JS issue that you might be able to help me with.
After a tween of container div width, I'm trying to append an inline div into the newly created container space using the onComplete() event.
The new div does not immediately get added to the space. On the next add action (that creates additional container width), the previously created div appears.
I'm trying to solve within the GreenSock context, but all ideas are welcomed.
Thanks!
TweenLite.to($('.list-item-container'), 1, {
width: $('.list-item-container').width() + 253,
delay: 0.25,
onComplete: function () {
var _item = document.createElement('div');
$(_item).addClass('list-item');
$('.list-item-container').append(_item);
}
});

It looks like the tween is complete before jQuery is ready.
$(function() {
TweenLite.to($('.list-item-container'), 1, {
width: $('.list-item-container').width() + 253,
delay: 0.25,
onComplete: function () {
var _item = $("<div/>");
$(_item).addClass('list-item');
$('.list-item-container').append(_item);
}
});
});
http://jsbin.com/lesire/1/
This also works for when I put it inside of a triggered event.
$("#button").on('click', function(){
TweenLite.to($('.list-item-container'), 1, {
width: $('.list-item-container').width() + 253,
delay: 0.25,
onComplete: function () {
var _item = $("<div/>");
$(_item).addClass('list-item');
$('.list-item-container').append(_item);
}
});
});
I created a JSBin that demonstrates it.
http://jsbin.com/zuyinu/5/
You might want to consider using an id to identify your list-item-container because
$('.list-item-container').append(_item);
will append _item to every element with the list-item-container class.
Good luck!

Related

How to make fixed header availabel at the focus an focusable element?

I have a fixed header, when user scroll to an offset 100px, a class add to header to make it fixed:
fixed-header {
position: fixed:
top: 0,
left: 0,
right: 0,
width: 100%;
}
$( window ).on( 'scroll', function() {
if ( $( window ).scrollTop() >= 100 ) {
navigation.classList.add('.fixed-header');
document.body.style.paddingTop = "100px" // prevent page jump when fixed header
} else {
navigation.classList.remove('.fixed-header');
document.body.style.paddingTop = "0"
}
});
On top of page, when user focus a link via tab key, I use focus event listener to check the fixed class in DOM to scroll focusable element to re-position it below fixed header:
const elements = [...document.querySelectorAll(
"a[href], area[href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled])"
)];
header = document.body.querySelector('.header-top'); // Fixed header height: 100px
const handleFocus = (e) => {
console.log(header.classList.contains('.fixed-header')); // This will false
if (header.classList.contains('.fixed-header')) {
var windowScrollTop = $( window ).scrollTop(),
focusableScrollTop = $(e.target).offset().top;
if (focusableScrollTop - windowScrollTop < 100) {
window.scrollTo({ top: focusableScrollTop - 100, behavior: 'smooth' });
}
}
};
for (const element of elements) {
element.addEventListener('focus', handleFocus, { once: true });
}
But focus callback handler fire before DOM repaint (when fixed class added to header), so it can not detect fixed class in DOM:
Wrap focus handler inside requestAnimationFrame()', don't work, if I wrap focus handler inside setTimeout()' function with 300ms, it will detect correct fixed class, but cause page scroll jump (focusable element scroll up and down).
for (const element of elements) {
element.addEventListener('focus', setTimeout(() => {
handleFocus()
}, 300), { once: true });
}
Any way to make header.classList.contains('.fixed-header') come true at the focusable link focused?
I hope someone know this issue and help to fix this problem. Thank you very much.

Want counter to start counting only when in viewport

I have a counter that is working perfectly fine, but it starts counting when the page is loaded - meaning that often when the user scrolls down, the counter has already stoped and therefore the effect is lost.
I've tried multiple suggestions found here on Stack Overflow, but none worked for my specific case.
Here's my code:
$('.counter').each(function() {
var $this = $(this),
countTo = $this.attr('data-count');
$({ countNum: $this.text()}).animate({
countNum: countTo
},
{
duration: 9000,
easing:'linear',
step: function() {
$this.text(Math.floor(this.countNum));
},
complete: function() {
$this.text(this.countNum);
//alert('finished');
}
});
});
Any tips I could incorporate into my code to ensure the counter starts counting only in viewport?
Many thanks in advance!
A good way to achive is using IntersectionObserver https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API to active your countdown when in viewport.
First, create an observer that will trigger when in view, and then start countdown.
const options = {
root: null,
rootMargin: '0px', //this determines when observer will trigger
threshold: 0
}
const observer = new IntersectionObserver((entries, observer) => {
entries.forEach((entry) => {
if(entry.isIntersecting) {
observer.unobserve(entry.target)
activateCountdown(entry.target) //call you function that will activate the counter
}
})
}, options);
Now, apply this observer to all counter that exists
$('.counter').each((index, element) => {
observer.observe(element)
})
function activateCountdown(countdown) {
//here you activate your counter
}

How to display Leaflet Static Labels for objects in a LayerGroup?

I'm trying to add a static label to a few CircleMarkers I've created. These markers are added to a LayerGroup and then added to the map. I've read that I need to call .showLabel() after I've added it to the object to the map. But since I am building the LayerGroup first, then adding it to the map I'm unsure of how to do this.
I thought about using L.LayerGroup.eachLayer but I'm unsure which object I would actually call the .showLayers() on. My code is below, any help is appreciated, thanks!
var jsonLayers = new L.LayerGroup();
jsonLayers.addLayer(L.geoJson(mapFeature.features[i], {
style: function (feature) {
return feature.properties && feature.properties.style;
},
onEachFeature: onEachFeature,
pointToLayer: function (feature, latlng) {
var newCircle = L.circleMarker(latlng, {
radius: 5,
fillColor: fColor,
color: "#000",
weight: 1,
opacity: 1,
fillOpacity: 0.8
});
newCircle.bindLabel(feature.properties.name, { noHide: true });
return newCircle;
}
}));
map.addLayer(jsonLayers);
It turns out that static labels are not supported on CircleMarkers. To solve this, I added code to Leaflet.label to allow this. I've also issued a pull request in case someone else would like to do the same thing I am doing.

backstretch next & previous buttons

I'm using the backstretch jquery to cycle images on my website. I can get the images to cycle fine, but I'm trying to add "next" and "previous" buttons, and I can't get them to work.
When I click on the next button, nothing happens.
My next button looks like this:
<a id="next" href="#"><img src="/images/arrow-right.png">
And I'm putting all my jquery code at the bottom of the page before the body close tag.
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script type="text/javascript" src="/js/jquery.backstretch.js"></script>
<script>
var images = [
"/images/backgrounds/Image01.jpg",
"/images/backgrounds/Image02.jpg",
"/images/backgrounds/Image03.jpg"
];
$(images).each(function() {
$('<img/>')[0].src = this;
});
// The index variable will keep track of which image is currently showing
var index = 0;
$.backstretch(images[index], {speed: 500});
$('#next').click(function(x) {
x.preventDefault();
$('body').data('backstretch').next();
});
$('#prev').click(function(x) {
x.preventDefault();
$('body').data('backstretch').prev();
});
</script >
Using firebug to debug, I get this:
TypeError: $(...).data(...) is undefined
$('body').data('backstretch').next();
The backstretch call was wrong.
Instead of this:
$.backstretch(images[index], {speed: 500});
I wanted this:
$.backstretch(images, {speed: 500});
$('body').data('backstretch').pause();
It's not that the next/prev buttons weren't working, it's that the initial call was passing the first image, not the set of images.
The second line (w/ pause in it) is there so the images don't change automatically, they only change when I hit the next/prev buttons.
Try this instead:
$('body').backstretch(images[index], {speed: 500});
$('#next').click(function(x) {
x.preventDefault();
$('body').data('backstretch').next();
});
$('#prev').click(function(x) {
x.preventDefault();
$('body').data('backstretch').prev();
});
I'm trying to fix this same problem of yours...
var rootUrl = "http://www.sagmeisterwalsh.com";
var images = [
rootUrl+"/images/u_work/Aizone-11.jpg",
rootUrl+"/images/u_work/Aizone-12.jpg",
rootUrl+"/images/u_work/Aizone-13.jpg"
];
$(images).each(function() {
$('<img/>')[0].src = this;
});
var index = 0;
var i = 1;
$.backstretch(images[index], {
fade: 750,
duration: 4000
});
$('#next').click(function(x) {
x.preventDefault();
$.backstretch(images[i++], {
fade: 750,
duration: 4000
});
$('#output').html(function(i, val) { return val*1+1 });
});
$('#prev').click(function(x) {
x.preventDefault();
$.backstretch(images[i--], {
fade: 750,
duration: 4000
});
$('#output').html(function(i, val) { return val*1-1 });
});
Try this:
http://jsfiddle.net/XejZV/

casperjs + modal popup

i'm wondering why cannot get casperjs to recognize the modal popup:
var casper = require('casper').create();
casper.start('http://www.zulutrade.com/trader/140682?Lang=en');
casper.waitForSelector("form[name=aspnetForm] button#main_BtnFollow",
function success() {
this.test.assertExists("form[name=aspnetForm] button#main_BtnFollow");
this.click("form[name=aspnetForm] button#main_BtnFollow");
},
function fail() {
this.test.assertExists("form[name=aspnetForm] button#main_BtnFollow");
});
casper.waitForPopup(/popup\.html$/, function() {
this.test.assertEquals(this.popups.length, 1);
});
casper.run(function() {this.test.renderResults(true);});
running the above gives me timeout in the waitForPopup part..
How to make this work and how to use casper.withPopup properly with the popup?
ah, are you using Resurrectio? in my experience, you need to tweak whatever script they generate. i mostly only use it for element names.
i'm only seeing asserts in your script. i think you need to tell casper to click on something before you see your modal. something like this maybe?
casper.then(function() {
this.clickLabel('.Follow', 'a');
});
casper.waitForSelector("#modal_popup", function() {
this.echo('see the modal!');
this.capture('screenshotofmodal.png', { top: 0, left:0, width:1000, height: 4000});
});
PS
using capture() is super helpful in troubleshooting your scripts. i have them as sort of like, breakpoints where i can easily see what's going on if a test that i know should be passing is failing.
I've found myself implementing crude timeouts to get casper to play nicely with modal interactions:
casper.start(uri).then(function() {
var slideshow = 'a.photo_gallery_icon_modal_launch';
casper.wait(2000, function(){
casper.thenEvaluate(function(sel) {
$elem = jQuery(sel).first();
$elem.click();
}, slideshow)
}).wait(1000, function(){
// wait a sec for modal to show up before taking pic
casper.capture('foo.png', {
top: 0,
left: 0,
width: 1280,
height: 1024
})
})
});
There is also waitForSelector but I have not had as much success with it, because the contents of my modal are also asynchronous making the usage of wait or even waitForUrl more appropriate.
http://docs.casperjs.org/en/latest/modules/casper.html#waitforselector