What I'm trying to do is find all 'section' elements, detect which one is in the viewport and apply a className to the current section. The className should be removed again when scrolling out of the viewport.
Here are the basics. I haven't included all methods and functions of the plugin, just what's needed to help answer the question:
// A simple forEach() implementation for Arrays, Objects and NodeLists.
// By Todd Motto
var forEach = function (collection, callback, scope) {
if (Object.prototype.toString.call(collection) === '[object Object]') {
for (var prop in collection) {
if (Object.prototype.hasOwnProperty.call(collection, prop)) {
callback.call(scope, collection[prop], prop, collection);
}
}
} else {
for (var i = 0, len = collection.length; i < len; i++) {
callback.call(scope, collection[i], i, collection);
}
}
};
// Determine if an element is the viewport or not
var _isInViewport = function (elem) {
var rect = elem.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
);
};
// Get all sections and
var _getSections = function () {
var sections = document.querySelectorAll('section');
forEach(sections, function (section) {
if (section._isInViewport) {
section(_isInViewport).classList.add('section-is-in-view');
alert('yest');
} else {
section(_isInViewport).classList.remove('not-in-view');
}
});
};
// The event handler
var _eventHandler = function (event) {
if (event.type === 'scroll') {
_getSections();
}
};
// Initialise the plugin
plugin.init = function (options) {
// Listen for scroll events and run event handler
document.addEventListener('scroll', _eventHandler, false);
}
Note: once it's working correctly, I plan to add some sort of debounce and throttler.
This immmediate thing that jumps out at me: this is wrong:
if (section._isInViewport) {
_isInViewport requires you to pass the element in as an argument, like so:
if (_isInViewport(section)) {
You also don't need to check the event type in the event handler. Since you're only calling that on scroll, you already know the event type is a scroll event. Instead of thing:
if (event.type === 'scroll') {
_getSections();
}
You want this:
_getSections();
Related
I have this code (below) on a buton to force my HTML5 game to fullscreen, but I'd like to have it turn back also with the button - right now it only works using ESC key. Is it possbile?
this.fsbtn.addEventListener("click", doFullscreen);
function doFullscreen() {
var i;
var elem = document.getElementById("animation_container");
var fs = ["requestFullscreen", "webkitRequestFullscreen", "mozRequestFullScreen", "msRequestFullscreen"];
for (i = 0; i < 4; i++) {
if (elem[fs[i]]) {
elem[fs[i]]();
break;
}
}
}
Sure it's possible. Change your doFullscreen function to a toggle one that checks if it's fullscreen or not:
function toggleFullscreen(event) {
var element = document.body;
if (event instanceof HTMLElement) {
element = event;
}
var isFullscreen = document.webkitIsFullScreen || document.mozFullScreen || false;
element.requestFullScreen = element.requestFullScreen || element.webkitRequestFullScreen || element.mozRequestFullScreen || function () {
return false;
};
document.cancelFullScreen = document.cancelFullScreen || document.webkitCancelFullScreen || document.mozCancelFullScreen || function () {
return false;
};
isFullscreen ? document.cancelFullScreen() : element.requestFullScreen();
}
Read the documentation here for fullscreen API
You can exit from fullscreen mode using functions listed below(for more read documentation)
In JS/HTML code you can add button with absolute position and high z-index. Write new click listener for added button and run cancelFullscreen function, that's all.
Example JS function for FullScreen mode:
function toggleFullScreen() {
if (!document.mozFullScreen && !document.webkitFullScreen) {
if (videoElement.mozRequestFullScreen) {
videoElement.mozRequestFullScreen();
} else {
videoElement.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT);
}
} else {
if (document.mozCancelFullScreen) {
videoElement.mozCancelFullScreen();
} else {
videoElement.webkitCancelFullScreen();
}
}
}
Working example you can see here: https://developer.mozilla.org/samples/domref/fullscreen.html
What is the most reliable and efficient way to find all elements having a scroll on a page?
Currently, I'm thinking about using element.all() with filter() comparing the height and scrollHeight attribute values:
element.all(by.xpath("//*")).filter(function (elm) {
return protractor.promise.all([
elm.getAttribute("height"),
elm.getAttribute("scrollHeight")
]).then(function (heights) {
return heights[1] > heights[0];
});
});
But I'm not sure about the correctness and performance of this approach.
This works with both horizontal and vertical scrollbars. The trick is detecting BOTH the too-wide/too-short AND if the computed CSS is going to allow you to display a scrollbar.
var ElementsWithScrolls = (function() {
var getComputedStyle = document.body && document.body.currentStyle ? function(elem) {
return elem.currentStyle;
} : function(elem) {
return document.defaultView.getComputedStyle(elem, null);
};
function getActualCss(elem, style) {
return getComputedStyle(elem)[style];
}
function isXScrollable(elem) {
return elem.offsetWidth < elem.scrollWidth &&
autoOrScroll(getActualCss(elem, 'overflow-x'));
}
function isYScrollable(elem) {
return elem.offsetHeight < elem.scrollHeight &&
autoOrScroll(getActualCss(elem, 'overflow-y'));
}
function autoOrScroll(text) {
return text == 'scroll' || text == 'auto';
}
function hasScroller(elem) {
return isYScrollable(elem) || isXScrollable(elem);
}
return function ElemenetsWithScrolls() {
return [].filter.call(document.querySelectorAll('*'), hasScroller);
};
})();
ElementsWithScrolls();
It will select the elements with overflowed and non-overflowed scrolls inside body tag:
$('body *').filter(function() {
return ($(this).scrollTop() != 0 || $(this).css('overflow') == 'scroll');
});
I'm using a Owl Carousel slider.
When you reach the last or first item of the slider you can still drag the slider although there aren't anymore items. Instead there is a bounce effect like when you pull down a native mobile app to refresh the content.
Demo: (link removed since the official docs aren't there anymore)
Drag the slider to the right and it will bounce back.
Is there a possibility to disable that bounce effect?
Yes, you can disable the bounce effect. I struggled finding it but I did it.
Just remove or comment out this lines code in owl.carousel.js:
base.newPosX = Math.max(Math.min(base.newPosX, minSwipe()), maxSwipe());
if (base.browser.support3d === true) {
base.transition3d(base.newPosX);
} else {
base.css2move(base.newPosX);
}
E.g.
owl.carousel.js
function dragMove(event) {
var ev = event.originalEvent || event || window.event,
minSwipe,
maxSwipe;
base.newPosX = getTouches(ev).x - locals.offsetX;
base.newPosY = getTouches(ev).y - locals.offsetY;
base.newRelativeX = base.newPosX - locals.relativePos;
if (typeof base.options.startDragging === "function" && locals.dragging !== true && base.newRelativeX !== 0) {
locals.dragging = true;
base.options.startDragging.apply(base, [base.$elem]);
}
if ((base.newRelativeX > 8 || base.newRelativeX < -8) && (base.browser.isTouch === true)) {
if (ev.preventDefault !== undefined) {
ev.preventDefault();
} else {
ev.returnValue = false;
}
locals.sliding = true;
}
if ((base.newPosY > 10 || base.newPosY < -10) && locals.sliding === false) {
$(document).off("touchmove.owl");
}
minSwipe = function () {
return base.newRelativeX / 5;
};
maxSwipe = function () {
return base.maximumPixels + base.newRelativeX / 5;
};
// base.newPosX = Math.max(Math.min(base.newPosX, minSwipe()), maxSwipe());
// if (base.browser.support3d === true) {
// base.transition3d(base.newPosX);
// } else {
// base.css2move(base.newPosX);
// }
}
Hope it helps.
I have this tested function below that works fine for fading an element in or out.
What do I gain by using JQuery?
Thanks
Effects.prototype.fade = function( direction, max_time, element )
{
var elapsed = 0;
function next() {
elapsed += 10;
if (direction === 'up')
{
element.style.opacity = elapsed / max_time;
}
else if (direction === 'down')
{
element.style.opacity = (max_time - elapsed) / max_time;
}
if (elapsed <= max_time) {
setTimeout(next, 10);
}
}
next();
};
Running a search on fadeIn() on the core jquery library I get one hit here:
jQuery.each({
slideDown: genFx( "show", 1 ),
slideUp: genFx( "hide", 1 ),
slideToggle: genFx( "toggle", 1 ),
fadeIn: { opacity: "show" },
fadeOut: { opacity: "hide" },
fadeToggle: { opacity: "toggle" }
}, function( name, props ) {
jQuery.fn[ name ] = function( speed, easing, callback ) {
return this.animate( props, speed, easing, callback );
};
});
Using the JQuery Source Viewer
function (prop, speed, easing, callback) {
var optall = jQuery.speed(speed, easing, callback);
if (jQuery.isEmptyObject(prop)) {
return this.each(optall.complete, [false]);
}
prop = jQuery.extend({},
prop);
return this[optall.queue === false ? "each" : "queue"](function () {
if (optall.queue === false) {
jQuery._mark(this);
}
var opt = jQuery.extend({},
optall),
isElement = this.nodeType === 1,
hidden = isElement && jQuery(this).is(":hidden"),
name, val, p, display, e, parts, start, end, unit;
opt.animatedProperties = {};
for (p in prop) {
name = jQuery.camelCase(p);
if (p !== name) {
prop[name] = prop[p];
delete prop[p];
}
val = prop[name];
if (jQuery.isArray(val)) {
opt.animatedProperties[name] = val[1];
val = prop[name] = val[0];
} else {
opt.animatedProperties[name] = opt.specialEasing && opt.specialEasing[name] || opt.easing || "swing";
}
if (val === "hide" && hidden || val === "show" && !hidden) {
return opt.complete.call(this);
}
if (isElement && (name === "height" || name === "width")) {
opt.overflow = [this.style.overflow, this.style.overflowX, this.style.overflowY];
if (jQuery.css(this, "display") === "inline" && jQuery.css(this, "float") === "none") {
if (!jQuery.support.inlineBlockNeedsLayout) {
this.style.display = "inline-block";
} else {
display = defaultDisplay(this.nodeName);
if (display === "inline") {
this.style.display = "inline-block";
} else {
this.style.display = "inline";
this.style.zoom = 1;
}
}
}
}
}
if (opt.overflow != null) {
this.style.overflow = "hidden";
}
for (p in prop) {
e = new jQuery.fx(this, opt, p);
val = prop[p];
if (rfxtypes.test(val)) {
e[val === "toggle" ? hidden ? "show" : "hide" : val]();
} else {
parts = rfxnum.exec(val);
start = e.cur();
if (parts) {
end = parseFloat(parts[2]);
unit = parts[3] || (jQuery.cssNumber[p] ? "" : "px");
if (unit !== "px") {
jQuery.style(this, p, (end || 1) + unit);
start = (end || 1) / e.cur() * start;
jQuery.style(this, p, start + unit);
}
if (parts[1]) {
end = (parts[1] === "-=" ? -1 : 1) * end + start;
}
e.custom(start, end, unit);
} else {
e.custom(start, val, "");
}
}
}
return true;
});
}
Usually you don't include a library like jQuery just for a single effect, but as a general purpose library in order to simplify things such as DOM manipulation, AJAX calls, setting CSS properties in a way that's cross-browser, in addition to applying effects (such as .fadeIn/.fadeOut) and other applications.
Tipically it's recommended you don't add jQuery for just a simple call. But my reasoning is that you are probably be going to exploit more and more of it's features in the long run, so I don't see a real reason not to use it.
On the subject of implementing your own fadeIn or fadeOut functions, you could look at the jQuery source and extract those methods, or make your own implementation from scratch. But given the fact that jQuery already implemented this method, I don't see why you would want to replicate it, other than for educational purposes.
The biggest reason to use JQuery over your custom code, in my opinion, is that you don't have to maintain the code for multiple browsers and multiple versions. JQuery does a good job of handling the quirks of the major browsers for you.
In addition, there are many other excellent uses for JQuery that you may want to use later.
Concerning the code, when you download JQuery: http://docs.jquery.com/Downloading_jQuery you can get the uncompressed version, which is intended to be readable.
I don't know of a simple way to get only those functions out of JQuery. Why not use the full library?
Can one use Window.Onscroll method to include detection of scroll direction?
If you record the scrollX and scrollY on page load and each time a scroll event occurs, then you can compare the previous values with the new values to know which direction you scrolled. Here's a proof of concept:
function scrollFunc(e) {
if ( typeof scrollFunc.x == 'undefined' ) {
scrollFunc.x=window.pageXOffset;
scrollFunc.y=window.pageYOffset;
}
var diffX=scrollFunc.x-window.pageXOffset;
var diffY=scrollFunc.y-window.pageYOffset;
if( diffX<0 ) {
// Scroll right
} else if( diffX>0 ) {
// Scroll left
} else if( diffY<0 ) {
// Scroll down
} else if( diffY>0 ) {
// Scroll up
} else {
// First scroll event
}
scrollFunc.x=window.pageXOffset;
scrollFunc.y=window.pageYOffset;
}
window.onscroll=scrollFunc
With jquery, you can also register a custom scroll event which supplies the scroll change as an argument to the event handler:
var previous_scroll = $(window).scrollTop();
$(window).on('scroll', function() {
var scroll = $(window).scrollTop(),
scroll_change = scroll - previous_scroll;
previous_scroll = scroll;
$(window).trigger('custom_scroll', [scroll_change]);
});
Then instead of scroll, bind to custom_scroll:
$(window).on('custom_scroll', function pos(e, scroll_change) {
console.log(scroll_change);
});
I had trouble making this work in ie8 (although it is compliant for ie9, FF and Chrome) - all scrolls seem to be detected as horizontal.
Here is a modified script demo that also works in ie8 and may cover a few more browsers.
function scrollFunc(e) {
function getMethod() {
var x = 0, y = 0;
if ( typeof( window.pageYOffset ) == 'number' ) {
x = window.pageXOffset;
y = window.pageYOffset;
}
else if( document.body && (document.body.scrollLeft || document.body.scrollTop ) ) {
x = document.body.scrollLeft;
y = document.body.scrollTop;
}
else if( document.documentElement && (document.documentElement.scrollLeft || document.documentElement.scrollTop ) ) {
x = document.documentElement.scrollLeft;
y = document.documentElement.scrollTop;
}
return [x, y];
}
var xy = getMethod();
var xMethod = xy[0];
var yMethod = xy[1];
if ( typeof scrollFunc.x == 'undefined' ) {
scrollFunc.x = xMethod;
scrollFunc.y = yMethod;
}
var diffX = scrollFunc.x - xMethod;
var diffY = scrollFunc.y - yMethod;
if( diffX<0 ) {
// Scroll right
} else if( diffX>0 ) {
// Scroll left
} else if( diffY<0 ) {
// Scroll down
} else if( diffY>0 ) {
// Scroll up
} else {
// First scroll event
}
scrollFunc.x = xMethod;
scrollFunc.y = yMethod;
}
window.onscroll=scrollFunc​