I am using the following function in a single page application in Angular. When I click on the menu item, I scroll to the relevant div.
scroll (el) {
this.sharedService.isClicked.next(true);
el.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
How do I check if the element has finished scrolling? I want to avoid the setTimeout function.
hope this helps..
var scrollIntoviewCompleted = false;
var currentVisible = false;
var onceVisible = false;
$(window).scroll(function(){
$.fn.isOnScreen = function(){
var element = this.get(0);
var bounds = element.getBoundingClientRect();
return bounds.top < window.innerHeight && bounds.bottom > 0;
}
if ($('.targetdiv').isOnScreen()) {
currentVisible = true;
onceVisible =true;
}
else
{
currentVisible = false;
}
if(onceVisible == true && currentVisible == false){
scrollIntoviewCompleted = true;
console.log("scrollIntoViewCompleted");
}
});
Based on an answer in this question I was able to make this for my tests... and this assumes that you are #Injecting window into your angular components
let win: Window;
const onScrollEnd = (fn: () => void): void => {
let scrollTimeout: number | undefined;
const listener = (): void => {
win.clearTimeout(scrollTimeout);
scrollTimeout = win.setTimeout(() => {
win.removeEventListener('scroll', listener);
fn();
}, 100);
};
win.addEventListener('scroll', listener);
};
beforeEach(() => {
//Standard component test setup stuff goes here...
win = TestBed.get('Window');
});
And then in my test I can do this:
it('should scroll the window down when...', (done: DoneFn) => {
component.shouldScroll = true;
fixture.detectChanges();
onScrollEnd(() => {
expect(win.scrollY).toBeGreaterThan(0);
done();
});
});
This worked for me (I declare this snippet public domain, feel free to re-use):
scrollAndDo = function(currPageXOffset,currPageYOffset) {
$('#SomeElement')[0].scrollIntoView({behavior: 'smooth',block:'nearest',inline: 'nearest'});
currPageXOffset = window.pageXOffset;
currPageYOffset = window.pageYOffset;
var scrollDone = setInterval(function () {
if ( currPageXOffset == window.pageXOffset && currPageYOffset == window.pageYOffset ) {
clearInterval(scrollDone);
console.log('I have finished scrolling');
}
currPageXOffset = window.pageXOffset;
currPageYOffset = window.pageYOffset;
},50);
};
scrollAndDo();
Related
I have two independent functions one to build a carousel and one to detect swipe Left or Right.
I cannot seem to find a way to use the function which navigates through the carousel into my Swipe Left or Right function (or vice versa).
I am at the end of my JS coding limits and struggling.
Here is the except:
this.doSomething = (event) => {
if (this.whatGestureDirection() == 'L') {
hpCarousel.move(-1);
} else {
hpCarousel.move(1);
}
}
I have tried alternatives; having google'd around there is a bind method. However, this is a bust too.
this.doSomething = (event) => {
if (this.whatGestureDirection() == 'L') {
let bindTheFunc;
bindTheFunc.bind(hpCarousel.move(-1));
return bindTheFunc;
} else {
let bindTheFunc;
bindTheFunc.bind(hpCarousel.move(1));
return bindTheFunc;
}
}
window.addEventListener('load', () => {
const hpCarousel = new carousel('area', 1);
const hpCarouselSwipe = new getSwipeX({elementId: 'homepage_carousel_wrapper'});
})
function getSwipeX({elementId}) {
this.e = document.getElementsByClassName(elementId)[0];
this.initialPosition = 0;
this.lastPosition = 0;
this.getTouchStart = (event) => {
event.preventDefault();
if (window.PointerEvent) {
this.e.setPointerCapture(event.pointerId);
}
return this.initalTouchPos = this.getGesturePoint(event);
}
this.getTouchMove = (event) => {
event.preventDefault();
return this.lastPosition = this.getGesturePoint(event);
}
this.getTouchEnd = (event) => {
event.preventDefault();
if (window.PointerEvent) {
this.e.releasePointerCapture(event.pointerId);
}
this.doSomething();
this.initialPosition = 0;
}
this.getGesturePoint = (event) => {
this.point = event.pageX
return this.point;
}
this.whatGestureDirection = (event) => {
const diffInPosition = this.initalTouchPos - this.lastPosition;
return (Math.sign(diffInPosition) > 0 ) ? `L` : `R`;
}
this.doSomething = (event) => {
if (this.whatGestureDirection() == 'L') {
**hpCarousel.move(-1);**
} else {
**hpCarousel.move(1);**
}
}
if (window.PointerEvent) {
this.e.addEventListener('pointerdown', this.getTouchStart, true);
this.e.addEventListener('pointermove', this.getTouchMove, true);
this.e.addEventListener('pointerup', this.getTouchEnd, true);
this.e.addEventListener('pointercancel', this.getTouchEnd, true);
}
}
I have a carousel script which a SO user kindly helped me with last week. It works great.
function carousel(id, index) {
this.slideIndex = index;
let carousel = document.getElementById(id);
this.slides = [...document.getElementsByClassName('homepage_carousel')];
let prev = carousel.getElementsByClassName('prev')[0];
let next = carousel.getElementsByClassName('next')[0];
prev.addEventListener('click', () => {
this.move(-1);
});
next.addEventListener('click', () => {
this.move(1);
});
this.hideAll = () => {
this.slides.forEach((slide) => {
slide.style.display = 'none';
});
};
this.show = () => {
this.hideAll();
this.slides[this.slideIndex - 1].style.display = 'flex';
}
this.move = (amount) => {
this.slideIndex += amount;
this.slideIndex = (this.slideIndex > this.slides.length) ? 1 : (this.slideIndex < 1) ? this.slides.length : this.slideIndex;
this.show();
}
this.show();
}
you can pass hpCarousel into hpCarouselSwipe as an argument.
window.addEventListener('load', () => {
const hpCarousel = new carousel('area', 1);
const hpCarouselSwipe = new getSwipeX({elementId: 'homepage_carousel_wrapper', carousel:hpCarousel});
})
function getSwipeX({elementId,carousel}) {
...
this.hpCarousel = carousel
}
And then later:
this.doSomething = (event) => {
if (this.whatGestureDirection() == 'L') {
this.hpCarousel.move(-1);
} else {
this.hpCarousel.move(1);
}
}
I did not test it, but it should work. You can pass a function into another function.
I am trying to make a function which will close my tooltip by 'click' on the parent element which returns me this tooltip by first click on it. Need to get the closing function exclusively for tooltip parent element. Right now everything is working, but i can also close my tooltip by clicking anywhere on body element.
`(function () {
function Tooltip(options) {
if (!options) options = {};
var self = this;
this.tooltips;
this.offset = 5;
this.beforeTooltip = options.beforeTooltip;
this.afterTooltip = options.afterTooltip;
this.tooltipWrapper = document.createElement('div');
this.status = false;
this.tooltip = function (elem) {
if (!elem.classList.contains('active')){
if (this.status) this.remElemActive();
if (this.beforeTooltip) this.beforeTooltip(elem);
elem.classList.add('active');
var coords = this.getCoords(elem);
this.tooltipWrapper.textContent = elem.dataset.tooltip;
this.tooltipWrapper.classList.add('active');
this.tooltipWrapper.style.top = coords.top - (this.tooltipWrapper.offsetHeight + this.offset) + 'px';
this.tooltipWrapper.style.left = (coords.left + coords.width / 2) - (this.tooltipWrapper.offsetWidth / 2) + 'px';
this.status = true;
if (this.status){
setTimeout(function () {
document.addEventListener('click', self.closeTipsBody, false);
}, 100)
}
if (this.afterTooltip) this.afterTooltip(elem)
}else {
elem.classList.remove('active');
}
};
this.closeTipsBody = function (e) {
if (self.tooltipWrapper === e.target || e.target.classList.contains('active')){
return false
}
self.closeTips();
};
this.closeTips = function () {
this.tooltipWrapper.classList.remove('active');
this.remElemActive();
this.status = false;
document.removeEventListener('click', self.closeTipsBody, false)
};
this.remElemActive = function () {
document.querySelector('.tooltip-js').classList.remove('active')
};
this.getCoords = function (elem) {
elem = elem.getBoundingClientRect();
return{
top: elem.top + window.pageYOffset,
left: elem.left + window.pageXOffset,
width: elem.width
}
};
this.init = function () {
document.addEventListener('DOMContentLoaded', function () {
this.tooltips = document.querySelectorAll('.tooltip-js');
this.tooltipWrapper.classList.add('tooltip-box');
document.querySelector('body').appendChild(this.tooltipWrapper);
for (var i = 0; i < this.tooltips.length; i++ ){
this.tooltips[i].addEventListener('click', function (e) {
e.preventDefault();
self.tooltip(this);
})
}
}.bind(this))
};
this.init();
}
window.Tooltip = Tooltip;
})();`
if you need any additional info, about what do i want to get to, text me.
I am trying to write this code in javascript from jquery
I already tried but cant seems to get equivalent to work.
$(document).ready(function() {
$('a').click(function() {
var selected = $(this);
$('a').removeClass('active');
$(selected).addClass('active');
});
var $a = $('.a'),
$b = $('.b'),
$c = $('.c'),
$d = $('.d'),
$home = $('.home'),
$about = $('.about');
$a.click(function() {
$home.fadeIn();
$about.fadeOut();
});
$b.click(function() {
$home.fadeOut();
$about.fadeIn();
});
});
The code works perfect in jQuery, but am trying to just use javascript. Its basically to add and remove class when a nav item is selected. I don't know if am explaining as clear as possible but am try to write the equivalent of this in javascript.
This is what I have tried.
var callback = function(){
var clickHandler1 = function() {
document.getElementById("home").classList.remove("home");
//var rem = document.getElementById("home");
//fadeOut(rem);
//alert("I am clicked B");
};
var anchors1 = document.getElementsByClassName("b");
for (var i = 0; i < anchors1.length; i++) {
var current = anchors1[i];
current.addEventListener('click', clickHandler1, false);
}
function fadeOut(el){
el.style.opacity = 1;
function fade() {
if ((el.style.opacity -= .1) < 0) {
el.style.display = "none";
} else {
requestAnimationFrame(fade);
}
})();
};
fadeIn(el, display){
el.style.opacity = 0;
el.style.display = display || "block";
(function fade() {
var val = parseFloat(el.style.opacity);
if (!((val += .1) > 1)) {
el.style.opacity = val;
requestAnimationFrame(fade);
}
})();
};
}
if ( document.readyState === "complete" || (document.readyState !== "loading" && !document.documentElement.doScroll)) {
callback();
} else {
document.addEventListener("DOMContentLoaded", callback);
}
The code in es6:
window.onload = function() {
const allLinks = document.querySelectorAll('a')
console.log(allLinks)
const removeAllClass = () => {
allLinks.forEach(link => {
link.classList.remove('active')
})
}
allLinks.forEach(element => {
element.addEventListener('click', event => {
removeAllClass()
element.classList.add('active')
})
})
let $a = document.querySelectorAll('.a'),
$b = document.querySelectorAll('.b'),
$home = document.querySelector('.home'),
$about = document.querySelector('.about');
$a.forEach(element => {
element.addEventListener('click', event => {
$about.classList.remove('fadeIn');
$home.classList.add('fadeIn');
})
})
$b.forEach(element => {
element.addEventListener('click', event => {
$home.classList.remove('fadeIn');
$about.classList.add('fadeIn');
})
})
}
you can see working on https://codepen.io/rwladyka/pen/qBBBpvy
document.getElementsByClassName('a') is the javascript equivalent of $('.a')
In order to differentiate between scroll and drag&drop on touch devices I decided to consider that drag event occurred if it follows long press.
Is there a way to make code below cleaner?
const listItem = document.getElementById("listItem");
listItem.addEventListener("touchstart", onTouchstart);
listItem.addEventListener("touchmove", onTouchmove);
listItem.addEventListener("touchend", onTouchend);
const longpress = false;
const longpressStart = 0;
const longpressChecked = false;
const LONGPRESS_DURATION = 100;
function onTouchstart() {
longpress = false;
longpressStart = Date.now();
}
function isLongPress() {
if (longpressChecked) {
return longpress;
}
if (Date.now() - longpressStart >= LONGPRESS_DURATION) {
longpress = true;
}
longpressChecked = true;
return longpress;
}
function onTouchmove() {
if (isLongPress()) {
// drag and drop logic
}
}
function onTouchend() {
longpress = false;
longpressStart = 0;
longpressChecked = false;
}
Thank you for help
You could beautify this through using some curried arrow functions:
const listen = (el, name) => handler => el.addEventListener(name, handler);
const since = (onStart, onEnd) => {
let last = 0;
onStart(() => last = Date.now());
onEnd(() => last = 0);
return time => Date.now() - last < time;
};
So you can just do:
const longPress = since(
listen(listItem, "touchstart"),
listen(listItem, "touchend")
);
listen(listItem, "touchmove")(evt => {
if(longPress(100)) {
//...
}
});
const listItem = document.getElementById("listItem");
listItem.addEventListener("touchstart", onTouchstart);
listItem.addEventListener("touchmove", onTouchmove);
listItem.addEventListener("touchend", onTouchend);
var onlongtouch = false;
function onTouchstart() {
timer = setTimeout(function() {
onlongtouch = true;
}, 100);
}
function onTouchmove() {
if(onlongtouch) {
// do something
}
}
function onTouchend() {
if (timer)
clearTimeout(timer);
onlongtouch = false;
}
welcome all ,
i have a problem with my images slider , it runs successfuly until poll script excuted then it stops , tried to combine both scripts didn't work also tried to use noConflict but in stops both of them .
this is the slider
(function ($) {
$.fn.s3Slider = function (vars) {
var element = this;
var timeOut = (vars.timeOut != undefined) ? vars.timeOut : 4000;
var current = null;
var timeOutFn = null;
var faderStat = true;
var mOver = false;
var items = $("#sliderContent .sliderImage");
var itemsSpan = $("#sliderContent .sliderImage span");
items.each(function (i) {
$(items[i]).mouseover(function () {
mOver = true
});
$(items[i]).mouseout(function () {
mOver = false;
fadeElement(true)
})
});
var fadeElement = function (isMouseOut) {
var thisTimeOut = (isMouseOut) ? (timeOut / 2) : timeOut;
thisTimeOut = (faderStat) ? 10 : thisTimeOut;
if (items.length > 0) {
timeOutFn = setTimeout(makeSlider, thisTimeOut)
} else {
console.log("Poof..")
}
};
var makeSlider = function () {
current = (current != null) ? current : items[(items.length - 1)];
var currNo = jQuery.inArray(current, items) + 1;
currNo = (currNo == items.length) ? 0 : (currNo - 1);
var newMargin = $(element).width() * currNo;
if (faderStat == true) {
if (!mOver) {
$(items[currNo]).fadeIn((timeOut / 6), function () {
if ($(itemsSpan[currNo]).css("bottom") == 0) {
$(itemsSpan[currNo]).slideUp((timeOut / 6), function () {
faderStat = false;
current = items[currNo];
if (!mOver) {
fadeElement(false)
}
})
} else {
$(itemsSpan[currNo]).slideDown((timeOut / 6), function () {
faderStat = false;
current = items[currNo];
if (!mOver) {
fadeElement(false)
}
})
}
})
}
} else {
if (!mOver) {
if ($(itemsSpan[currNo]).css("bottom") == 0) {
$(itemsSpan[currNo]).slideDown((timeOut / 6), function () {
$(items[currNo]).fadeOut((timeOut / 6), function () {
faderStat = true;
current = items[(currNo + 1)];
if (!mOver) {
fadeElement(false)
}
})
})
} else {
$(itemsSpan[currNo]).slideUp((timeOut / 6), function () {
$(items[currNo]).fadeOut((timeOut / 6), function () {
faderStat = true;
current = items[(currNo + 1)];
if (!mOver) {
fadeElement(false)
}
})
})
}
}
}
};
makeSlider()
}
})(jQuery);
and this is the poll script
window.onload = function() {
$(".sidePollCon").load("ar_poll.html", function(r, s, xhr) {
if (s == "error") {
$(".sidePollCon").load("poll.html");
} else {
$(".vote_booroo").html("صوت الان");
$(".viewresults").html("شاهد النتيجة");
$("fieldset p").html("");
$(".results_booroo p").html("");
$(".result_booroo").attr("src", "../images/poll_color.jpg");
}
});
};
One potential problem could be the window.onload assignment. It is very prone to conflict.
Every time you do window.onload = the previous assignemnt will be overridden. See demo here:
The output shows that the first window.onload assignment never gets called, while the jQuery alternative does get called.
jQuery.noConflict does little in this regard. All it does is to prevent override the $ symbol so that another lib can use it.
So if you are also using the window.onload event to invoke the slider, then you have conflict. You can easily solve this problem by using the jquery format:
$(window).load(function() {
...
});
However usually you would tie the event to $(document).load(function(){...}); or in short form: $(function(){...}).
So for your poll that would be:
$(function(){
$(".sidePollCon").load("ar_poll.html", function(r, s, xhr) {
if (s == "error") {
$(".sidePollCon").load("poll.html");
} else {
$(".vote_booroo").html("صوت الان");
$(".viewresults").html("شاهد النتيجة");
$("fieldset p").html("");
$(".results_booroo p").html("");
$(".result_booroo").attr("src", "../images/poll_color.jpg");
}
});
});
Hope that helps.
resolving conflicts in jquery (possibly with another JS library .. like script.aculo.us) can be resolved using noconflict()
http://api.jquery.com/jQuery.noConflict/
$.noConflict();
but make sure that u have no error in your javascript code itself. use firebug and
console.log('') to test your script.