Resizing function appends elements multiple times - javascript

I have a bit of code that needs to detect scrollbars on resize and load. This is straightforward enough, the issue arises when I run the append() function, it appends the elements over and over again.
Below is the jQuery I am using (the demo is here)
$.fn.hasScrollBar = function() {
return this.get(0).scrollWidth > this.width();
}
$(window).on("resize", function () {
var tableWidth = $("table").width();
var wrapper = "<div class='top-scroll'><div class='inner'></div></div>";
var instruction = "<span class='instructions'>Scroll left to see more information</span>";
// This sets the overflow message and a placeholder width to match the table
if( $('.no-overflow').hasScrollBar()){
$('.no-overflow').before(wrapper);
$('.top-scroll').before(instruction);
$('.top-scroll').children().attr("style", "width:" + tableWidth + "px;");
}
// This synchronises the scrollbars
$(".top-scroll").scroll(function(){
$(".no-overflow")
.scrollLeft($(".top-scroll").scrollLeft());
});
$(".no-overflow").scroll(function(){
$(".top-scroll")
.scrollLeft($(".no-overflow").scrollLeft());
});
}).resize();
I'd like the append to only happen once and also remove when there are no scrollbars present.

To check for scrollbars:
if ($(document).height() > $(window).height()) {
//There is a vertical scrollbar
} else {
//There is no vertical scrollbar
}
To only create one .top-scroll:
if( $('.no-overflow').hasScrollBar() && $('.top-scroll').length < 1) { // Check .top-scroll doesn't exists currently
$('.no-overflow').before(wrapper);
$('.top-scroll').before(instruction);
$('.top-scroll').children().attr("style", "width:" + tableWidth + "px;");
}
I would also put your 3 variable declarations within your if statement so they will only be set if the statement is true. Unless of course you are using them elsewhere.

Related

javascript scrollTop doesn't seem to work

I use a technique where I have a table within a div so as to limit the space covered by the table and scroll instead.
Within the table are checkboxes. These checkboxes effect how the table is rendered. When one is clicked, the table is re-rendered within the div. This always causes the scrollbar to go back to the top which is annoying.
So after I render the table in javascript I do a setTimeout call to asynchronously call a function that sets the scrollTop value back to where it was before the re-render.
Here's the code snipit in question:
Note: (ge() == geMaybe() == document.getElementById())
o.renderAndScroll = function() {
var eTestSection = geMaybe(o.id + '-testSection');
var scrollTop = 0;
if (eTestSection) {
scrollTop = eTestSection.scrollTop;
}
o.render();
if (eTestSection) {
setTimeout(
function() {
console.log('Scrolling from ' + ge(o.id).scrollTop + ' to ' + scrollTop);
ge(o.id).scrollTop = scrollTop;
console.log('Scrolled to ' + ge(o.id).scrollTop);
},
1000);
}
}
My console log output is this each time I change a checkbox state:
Scrolling from 0 to 1357
Scrolled to 0
Any other way to make this work? Note that I made the timeout a full second just to make sure the render was moved to the DOM by the time my scroll code is called. I am using chrome mainly but need it to eventually work cross-browser. I don't use jQuery. If I try to catch the onscroll event in the debugger or even log stuff from an onscroll handler, the chrome debugger crashes when the scrollbar is moved with the mouse.
The correct code is:
o.renderAndScroll = function(fForce) {
var scrollTop = 0;
var eTestSection = geMaybe(o.id + '-testSection');
if (eTestSection) {
scrollTop = eTestSection.scrollTop;
}
o.render(fForce);
setTimeout(
function() {
// re-lookup element after being rendered
var eNewTestSection = ge(o.id + '-testSection');
eNewTestSection.scrollTop = scrollTop;
},
1);
};

Execute something while element is in view

I am using the Jquery inview plugin and I am trying to load some elements whenever the user reached the footer of the page. While doing this, I discovered a bug where if the user holds the scroll-click and drags the mouse towards the bottom, in some cases the elements will not load anymore until the footer is out of the view and then back into the view.
Here is the function that I have so far to load the elements when the footer is in the viewport:
//Infinite load function. Uses jquery.inview
$scope.addMoreElements = function(){
$scope.limitElementsPerPage += 16;
$('.footer').on('inview', function(event, isInView) {
if (isInView) {
// element is now visible in the viewport
$scope.limitElementsPerPage += 16;
} else {
// element has gone out of viewport
//do nothing
}
});
};
I am using Angularjs as well as jQuery for this project. Essentially, what I think I need is something that checks at about 1-2 seconds if the element is still in view. I am not exactly sure I should do this at the moment. This is what I tried to do to solve this issue:
$scope.$watch($('.footer'), function(){
$('.footer').on('inview', function(event, isInView) {
setTimeout(function(){
while(isInView){
console.log('test')
}
}, 1000);
});
});
This unfortunately, will crash the browser (I am not sure how I would go about doing this with the setTimeout or the other related functions).
Any help or ideas on how to do this would be greatly appreciated.
Thank you in advance.
InView adds a new event for elements, that triggers when the element enters the viewport. Probably some times you just have the footer in the viewport at all times, so that is why it fails.
I think you need to redesign the logic of the page to use the 'scroll' event on whatever element contains the added items and scrolls for the infinite view and in that event to check if the footer is in the viewport, not if it enters.
Personally I use this extension for checking if it is in the viewport:
(function($) {
$.inviewport = function(element, settings) {
var wh=$(window).height();
var wst=$(window).scrollTop();
var et=$(element).offset().top;
var eh=$(element).height();
return !(wh + wst <= et)&&!(wst >= et + eh);
};
$.extend($.expr[':'], {
"in-viewport": function(a, i, m) {
return $.inviewport(a);
}
});
})(jQuery);
Here are couple of functions you can use:
var getScrollY = function(){
var supportPageOffset = window.pageXOffset !== undefined;
var isCSS1Compat = ((document.compatMode || "") === "CSS1Compat");
var y = supportPageOffset ? window.pageYOffset : isCSS1Compat ?
document.documentElement.scrollTop : document.body.scrollTop;
return y;
}
function get_elem_y( elem ) {
var box = elem.getBoundingClientRect();
return box.top + getScrollY();
}
And then you can listen to the scroll event, assume footer is something like <div id="footer">...</div>
var footer = document.getElementById("footer"); // get footer
var b_foot_visible = false;
window.addEventListener("scroll", function() {
var y = get_elem_y(footer);
var pageHeight = ( window.innerHeight || document.body.clientHeight);
if((getScrollY() + pageHeight) > y ) {
// footer is visible
if(!b_foot_visible) {
// TODO: add something
b_foot_visible = true;
}
} else {
// footer is not visible
if(b_foot_visible) {
// TODO: remove something
b_foot_visible = false;
}
}
});
Thus, when the scrollY + pages height reaches the footer elements Y coordinate you can do something to display things for the footer.
You might also add check in the beginning to test if the footer is already visible.

add element offset to jQuery offset calculations

I am rather new to jquery and i'm trying to find the right offset for a div element inside the body. I want to make this div element sticky whenever I scroll down and pass the top offset of this element.
I followed this tutorial: https://www.youtube.com/watch?v=utonytGKodc and it works but I have a metaslider in my header and the width/height of this element is left out of the calculations to find the right offset....
the result is that my element becomes a sticky element way to soon, is there a way I can manualy add the sliders coordinates (offset) to the offset calculation of the element i want to make sticky?
var offerteOffset = jQuery(".agendawrap").offset().top //+ metaslider coordinates??;
alert(offerteOffset);
jQuery(window).scroll(function() {
var scrollPos = jQuery(window).scrollTop();
if (scrollPos >= offerteOffset) {
jQuery(".agendawrap").addClass("fixed");
} else {
jQuery(".agendawrap").removeClass("fixed");
}
});
I cant believe people make such bad tutorials.
First of all: dont write jQuery all the time. Have a look at this thread.
Basically it says: use an invoking function with an own scope:
(function($) { /* all your jQuery goes here */ })(jQuery);
So you can just type $ instead of jQuery.
To your original question:
(function($) {
$(function() { // document ready...
var scrollTolerance = 50,
agendawrap = $(".agendawrap"),
offerteOffset = agendawrap.offset().top;
$(window).on('scroll', function() {
var scrollPos = $(window).scrollTop();
// OR: if (scrollPos - scrollTolerance >= offerteOffset) {
if (scrollPos + scrollTolerance >= offerteOffset) {
agendawrap.addClass("fixed");
}
else {
agendawrap.removeClass("fixed");
}
});
});
})(jQuery);

make both editors in the same size when they autogrowing

I'm using two CKEDITOR's editors in one page, and I them to be in the same size all the time. I'm using the auto grow plugin, so I tried it:
CKEDITOR.plugins.addExternal( 'autogrow', location.href + 'ckeditor/autogrow/', 'plugin.js' );
var e1 = CKEDITOR.replace("heb_editor", {extraPlugins: 'autogrow'});
var e2 = CKEDITOR.replace("eng_editor", {extraPlugins: 'autogrow'});
e1.on("resize", r);
e2.on("resize", r);
function r(){
if($("#cke_1_contents").height() > e2.config.height)
$("#cke_2_contents").height($("#cke_1_contents").height());
else
$("#cke_1_contents").height($("#cke_2_contents").height());
}
it didn't worked. It did resized the second editor to the size of the first one, but it didn't resized the first to the size of the second when it was needed. What to do?
Here is a JSfiddle: http://jsfiddle.net/povw33x7/
Forget all I said before (I deleted it, but you can still see it in the revision history).
Using some code I found on this Web site, you can calculate the height of the box. Now, you just need to apply that to update the box heights on resize:
function getBoxHeight(boxId) {
// using a function to get the height of the box from ==>
var ckeditorFrame = $('#' + boxId + ' iframe');
var innerDoc = (ckeditorFrame.get(0).contentDocument) ? ckeditorFrame.get(0).contentDocument : ckeditorFrame.get(0).contentWindow.document;
var messageHeight = $(innerDoc.body).height();
return messageHeight ? messageHeight : 0;
}
function r() {
if (getBoxHeight("cke_1_contents") > getBoxHeight("cke_2_contents")) {
$("#cke_2_contents").height($("#cke_1_contents").height());
} else {
$("#cke_1_contents").height($("#cke_2_contents").height());
}
}
As you can see on this JSFiddle: http://jsfiddle.net/povw33x7/3/. This solution is cleaner than the other one, although it still has a glitch as it may leave an extra empty space (the height of a line) in one of the boxes.

infinite-scroll jquery plugin

I am trying to set up infinite-scroll on a site I am developing with Coldfusion, I am new to javascript and jquery so I am having some issues wrapping my head around all of this. Do I need to have pagination on my site in order to use the infinite-scroll plugin, or is there a way to do it with out it?
You do not need infinite scroll plug-in for this. To detect when scroll reaches end of page, with jQuery you can do
$(window).scroll(function () {
if ($(window).scrollTop() >= $(document).height() - $(window).height() - 10) {
//Add something at the end of the page
}
});
Demo on JsFiddle
I'm using Hussein's answer with AJAX requests. I modified the code to trigger at 300px instead of 10px, but it started causing my appends to multiply before the AJAX request was finished since the scroll call triggers much more frequently in a 300px range than a 10px range.
To fix this, I added a trigger that would be flipped on successful AJAX load. My code looks more like this:
var scrollLoad = true;
$(window).scroll(function () {
if (scrollLoad && $(window).scrollTop() >= $(document).height() - $(window).height() - 300) {
scrollLoad = false;
//Add something at the end of the page
}
});
then in my AJAX response, I set scrollLoad to true.
I built on top of Hussein's little example here to make a jQuery widget. It supports localStorage to temporarily save appended results and it has pause functionality to stop the appending every so often, requiring a click to continue.
Give it a try:
http://www.hawkee.com/snippet/9445/
$(function(){
$(window).scroll(function(){
if($(document).height()<=$(window).scrollTop()+$(window).height()+100){
alert('end of page');
}
});
});
Some one asked for explanation so here is the explanation
here $(document).height()-->is the height of the entire document.In most cases, this is equal to the element of the current document.
$(window).height()-->is the height of the window (browser) means height of whatever you are seeing on browser.
$(window).scrollTop()-->The Element.scrollTop property gets or sets the number of pixels that the content of an element is scrolled upward. An element's scrollTop is a measurement of the distance of an element's top to its topmost visible content. When an element content does not generate a vertical scrollbar, then its scrollTop value defaults to 0.
$(document).height()<=$(window).scrollTop()+$(window).height()+100
add $(window).scrollTop() with $(window).height() now check whether the result is equal to your documnet height or not. if it is equal means you reached at the end.we are adding 100 too because i want to check before the 100 pixels from the bottom of document(note <= in condition)
please correct me if i am wrong
I had same problem but didn't find suitable plugin for my need. so I wrote following code. this code appends template to element by getting data with ajax and pagination.
for detecting when user scrolls to bottom of div I used this condition:
var t = $("#infiniteContent").offset().top;
var h = $("#infiniteContent").height();
var ws = $(window).scrollTop();
var dh = $(document).height();
var wh = $(window).height();
if (dh - (wh + ws) < dh - (h + t)) {
//now you are at bottom of #infiniteContent element
}
$(document).ready(function(){
$.getJSON("https://jsonplaceholder.typicode.com/comments", { _page: 1, _limit:3 }, function (jsonre) {
appendTemplate(jsonre,1);
});
});
function appendTemplate(jsonre, pageNumber){
//instead of this code you can use a templating plugin like "Mustache"
for(var i =0; i<jsonre.length; i++){
$("#infiniteContent").append("<div class='item'><h2>"+jsonre[i].name+"</h2><p>"+jsonre[i].body+"</p></div>");
}
if (jsonre.length) {
$("#infiniteContent").attr("data-page", parseInt(pageNumber)+1);
$(window).on("scroll", initScroll);
//scroll event will not trigger if window size is greater than or equal to document size
var dh = $(document).height() , wh = $(window).height();
if(wh>=dh){
initScroll();
}
}
else {
$("#infiniteContent").attr("data-page", "");
}
}
function initScroll() {
var t = $("#infiniteContent").offset().top;
var h = $("#infiniteContent").height();
var ws = $(window).scrollTop();
var dh = $(document).height();
var wh = $(window).height();
if (dh - (wh + ws) < dh - (h + t)) {
$(window).off('scroll');
var p = $("#infiniteContent").attr("data-page");
if (p) {
$.getJSON("https://jsonplaceholder.typicode.com/comments", { _page: p, _limit:3 }, function (jsonre) {
appendTemplate(jsonre, p);
});
}
}
}
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<div id="infiniteContent"></div>
If you have a scrollable element, like a div with scroll overflow, but no scrollable document/page, you can take this way.
$(function () {
var s = $(".your-scrollable-element");
var list = $("#your-table-list");
/* On element scroll */
s.scroll(function () {
/* The scroll top plus element height equals to table height */
if ((s.scrollTop() + s.height()) == list.height()) {
/* you code */
}
});
});
I wrote this function using Hussein and Nick's ideas, but I wanted it to use promises for the callback. I also wanted the infinite scrolling area to be on a fixed div and not just the window if the div is sent into the options object. There is an example of that in my second link below. I suggest using a promise library like Q if you want to support older browsers. The cb method may or may not be a promise and it will work regardless.
It is used like so:
html
<div id="feed"></div>
js
var infScroll = infiniteScroll({
cb: function () {
return doSomethingPossiblyAnAJAXPromise();
}
});
If you want the feed to temporarily stop you can return false in the cb method. Useful if you have hit the end of the feed. It can be be started again by calling the infiniteScroll's returned object method 'setShouldLoad' and passing in true and example to go along with the above code.
infScroll.setShouldLoad(true);
The function for infinite scrolling is this
function infiniteScroll (options) {
// these options can be overwritten by the sent in options
var defaultOptions = {
binder: $(window), // parent scrollable element
loadSpot: 300, //
feedContainer: $("#feed"), // container
cb: function () { },
}
options = $.extend(defaultOptions, options);
options.shouldLoad = true;
var returnedOptions = {
setShouldLoad: function (bool) { options.shouldLoad = bool; if(bool) { scrollHandler(); } },
};
function scrollHandler () {
var scrollTop = options.binder.scrollTop();
var height = options.binder[0].innerHeight || options.binder.height();
if (options.shouldLoad && scrollTop >= (options.binder[0].scrollHeight || $(document).height()) - height - options.loadSpot) {
options.shouldLoad = false;
if(typeof options.cb === "function") {
new Promise(function (resolve) {resolve();}).then(function() { return options.cb(); }).then(function (isNotFinished) {
if(typeof isNotFinished === "boolean") {
options.shouldLoad = isNotFinished;
}
});
}
}
}
options.binder.scroll(scrollHandler);
scrollHandler();
return returnedOptions;
}
1 feed example with window as scroller
2 feed example with feed as scroller

Categories

Resources