JQuery Scrollable scrolls further than actually wanted - javascript

I'm using JQuery Scrollables to display an image gallery, see http://www.mba-europe.de/lehr_veroff.html
In my setup, I have a .bscrollable DIV that contains a .bitems DIV that contains all the .item DIV's in analogy to .scrollable, .items and .item from the JQuery Scrollables demo page.
However, as you can see, scrolling doesn't stop as soon as the last element in the list is reached. Instead, the user can scroll further until only the last element is visible, followed by (in my case) three empty slots.
How do I avoid scrolling further, such that there are always no less than four elements visible?
Best regards,
Philip

You can use scrollable's api to achive that like this;
var $scrollables = $('.bscrollable').scrollable({
size: 4,
mousewheel: true,
vertical: false,
horizontal: true
}).data('scrollable');
$scrollables.onBeforeSeek(function(e, i) {
var threshold = $scrollables.getSize() - 4;
if(i > threshold) {
e.preventDefault();
}
});
Working example

Related

JavaScript function to manipulate CSS custom properties not working on $(window).resize()

I have a fixed header with :target in-page anchors, and need to adjust the property values dynamically via JavaScript or JQuery so as to maintain the relevant :target's position directly under the header when the window is resized, while adapting to the changes in both the previous section's .container height and the .header_container height that occur with resizing.
The simplest solution seems to be a ::before pseudo-element for the :target pseudo-class, and to then utilize CSS custom properties to dynamically modify the style properties.
I have no trouble correctly positioning the :target with the below function when the page is loaded (or reloaded), or correctly position the first :target on $(window).resize(), however it's failing to do the same for the remaining targets on $(window).resize().
Fiddles
Simplified Code: https://jsfiddle.net/chayanyc/g6p3549s/
Responsive Design (Simplified): https://jsfiddle.net/chayanyc/wuk92dns/
Code Snippets
CSS:
.header_container {height: 98px; margin: 0; padding: 0; position: fixed; top: 0; display: block; z-index: 100;}
.main {margin-top: 98px; width: 100%;}
:target::before {height: var(--target_position1); margin-top: var(--target_position2); content: ""; display: block; visibility: hidden;}
JavaScript:
var headerHeight;
function setTarget() {
headerHeight = document.getElementById('header').offsetHeight;
headerHeight1 = headerHeight + "px";
document.documentElement.style.setProperty('--target_position1', headerHeight1);
document.documentElement.style.setProperty('--target_position2', '-' + headerHeight1);
}
$(window).resize(function () {
setTarget();
});
$(document).ready(function () {
setTarget();
});
There is no complete solution to this Problem,
because you want the target element stay on place on document resize, but if the user do a scroll on his page, it is not possible to know where staying on the same first word of the first line on display.
So here, i just replace on the same target on top when user resize his document, even if he had done a scroll just before.
no need of this CSS part (remove it)
:target::before {margin: 0; content: ""; dis.....
and change your jQuery to:
$(document).ready(function () {
// global info for menu -> target elememt
var InfoTarget = { ID: null, tempo:300 }
$('a').click(function(evt){
InfoTarget.ID = $(this).attr('href') // possible target elm
// check if InfoTarget.ID exist on page
let nbElements = 0
try { nbElements = $(InfoTarget.ID).length }
catch(err) { nbElements = 0 }
if ( nbElements != 1 ) {
InfoTarget.ID = null // not target element found
}
else {
evt.preventDefault() // disable auto scroll to target element
$('html').animate({
scrollTop: ($(InfoTarget.ID).offset().top - $('#header').outerHeight(true))
}, InfoTarget.tempo );
}
});
$(window).resize(function (){
if (InfoTarget.ID) { // if InfoTarget.ID exist <=> InfoTarget.ID != null
$('html').animate({
scrollTop: ($(InfoTarget.ID).offset().top - $('#header').outerHeight(true))
}, InfoTarget.tempo);
}
});
});
My code speaks for itself, but here is a complete explanation:
the principle is simple: the function target css activates on a click on a link <a href="#..."> to trigger a scroll of the page towards the element having for id = to that contained in the initial href.
therefore this code intercepts any click on a link on the page and must first determine whether it is a link to an anchor or not.
To determine if this is a link to an anchor on the page, it simply tests whether an element of the page has this value as this ID, (// check if InfoTarget.ID exists on page).
As this kind of test can also generate an error, this test is placed in a try / catch.
If the result is indeed an anchor, then the action of the click is canceled, (with evt.preventDefault()) which prevents the browser from triggering its automatic scroll to the link;
the reference link is kept in an object variable (global)
var InfoTarget = {ID: null, tempo: 300}
seen on: InfoTarget.ID = $(this).attr('href') // possible target elm
the rest is simple, you have to scroll down to the anchor.
Depending on the width of the page and the previous elements, browsers continuously recalculate the position of each tag present on a page and jQuery can be retrieved this offset position by $(element).offset().Top
as there is a menu bar on your page that masks the top of the page, you must deduct its height from the position in scroll (= $ ('# header'). outerHeight (true))
a scroll = 0 will force a move to the top of the page
a scroll = $(element).offset().top places the element at the top of the page
to which we must deduct the height of the #header
the complete formula is
scrollTop: ( $(InfoTarget.ID).offset().top - $('#header').outerHeight(true) )
this command is placed in a jQuery.animate, for a visually smoother move, and uses the InfoTarget.tempo value as the duration for this animation.
During a resize of the page, and to the extent that a link having a target has been previously clicked (therefore always active) then the same type of scroll is triggered.
The different jQuery methods used are all explained in the jQuery doc (for example: https://api.jquery.com/outerHeight/ )
New Solution -- Lundi 14 oct 2019 / 01:00 (in the night of sunday / monday)
this script must be placed after all the html elements of the body
// scroll to target upon window.location.hash
$(window).on('hashchange', function() {
$('.TargetMark').removeClass('TargetMark')
$(window.location.hash).addClass('TargetMark')
setTimeout( scrollTop2, 220 ) // scroll to target after browser auto scrolling conflit
})
function scrollTop2() {
if ($('.TargetMark').length===1) { // if target exist
$('html').animate({
scrollTop: ($('.TargetMark').offset().top - $('#header').outerHeight(true))
}, 100);
}
}
In this version the target element is added a class (TargetMark) allowing to find it when window resize
ending part
$(document).ready(function () {
//...
// ---------------------------> no call to scrollTop();
//...
});
$(window).resize(function () {
//...
scrollTop2();
//...
});
about toggleMenu conflict:
function toggleMenu() {
$('.navbar-toggle').on('click', function () {
if ($("#js-menu").is(".expand")) {
$("#js-menu").toggleClass("expand");
$("#submenu").removeClass("active_sub").addClass("inactive_sub");
} else {
$("#js-menu").toggleClass("expand");
$("#submenu").removeClass("inactive_sub").addClass("active_sub");
}
resetTarget();
setTimeout( scrollTop2, 220 ) // scroll to target after browser auto scrolling conflit
});
}
I spent a lot of my time on your question, I studied differents approaches and the different automatisms put at work by the navigators themselves and which is necessary to fight to get the result you'r looking for. I came to the conclusion that the problem first came from the architecture of your page.
The fact that the menu ("#header") covers the page ("#main") is a major flaw that prevents to have an effective JS code for your question.
The call on the hash of an anchor triggers one or more scrolls of the page, the resize of the page also entails a scroll calculation because by changing size on the screen, it also changes the size of the page. page (reducing the size of the screen by half makes the page size double), it is the same by changing the size of the font, it also changes the size in page.
Whenever the page size changes, the browser must recalculate a lot of things and some of these mechanisms can trigger one or more scrolls.
What you are asking here is to recalculate a page positioning according to an element of which we can not be certain that it is completely established because this process is executed in parallel with other processes of the browser which can change useful values.
Plus the fact that some of the browser processes also work to scroll the page and that it can be the last done!
So the fact that there is an overlap between the menu and the page add more complexity and makes the possibility of a solution impossible.
Change your layout and 3/4 of your problem will be fixed.
Resize is firing, offset height is not changing. Setting the same value over and over again, yields no change. You might check this:
see the value change
I used the logo for output:
$('.logo').text(headerHeight + ' -' + i++);
You want to scroll down to the one div selected by target without having it to be overlapped by your nav?
.. then extend the areas. see here
add positive margin-top and negative padding-top.
.... to compensate for any nav size changes, use media queries to change your css vars.

Using flickity to resize as tabs

So I have a project now that is basically defusing bugs and this one in particular is a very annoying case. Flickity is supposed to be "resizing" a specific elements within <div> and working as tabs in the bottom to scroll to the specific informational element. However, all the text is just mashing together.
.
I have figured out, though, when you resize the browser, it works correctly and puts everything in its place by showing one <div> at a time and using buttons at the bottom to switch between <div>.
Here is the flickity part of the jQuery:
modalPopup(e, function() {
if ($(".key .active").not("#resetFilters").length) {
$(".button-group").find($(".key .active").data("filter"));
}
else {
$('.button-group li:first-child').addClass('is-selected');
}
$('.button-group').on( 'click', 'li', function() {
var index = $(this).index();
$(this).addClass('is-selected').siblings('.is-selected').removeClass('is-selected');
gallery.flickity( 'select', index );
});
var gallery = $('.chapters').flickity({
// options
//imagesLoaded: true,
//percentPosition: false,
cellAlign: 'center',
contain: true,
prevNextButtons: false,
pageDots: false,
resize: true
});
});
Where modalPopup() is a function that loads in the information.
Any help or suggestions, are highly appreciated!
I have figured out the issue. The issue is that the width of the element I was using flickity on was at 0px width initially using CCS transitions. Flickity thought the width of the parent element was at 0px and therefore didn't expand out its width elements.
So if you use CSS transitions with flickity resize, make sure you have a width defined.

Add item to top of list and scroll down

I feel this should be simple, but I can't get my head round how to do it.
I want to add new items to the top of a list, and have the list scroll down to reveal the new item.
The closest I've got so far is here: http://jsfiddle.net/philgyford/cmsh0zoz/4/ This creates and adds a new list item by doing:
var content = 'Hello';
if (Math.random() < 0.3) {
content += '<br/>Another line';
};
$('<li>').html(content)
.hide()
.prependTo( $('ul') )
.slideDown('slow', function() {
// Remove final element:
$('ul').find('li:gt(9)').remove();
});
That reveals the new item by having it slideDown, which isn't quite what I want - I want it to be as if the new item has appeared behind the "Test list" heading, and then the whole list slides down to reveal it.
Note that list items have varying heights, which complicates things slightly.
Instead of using slideDown, considering animating the ul's top margin, starting with the negative height of the newly-added element (including padding).
First, add this to your h1 style:
position: relative;
That effectively puts the h1' on top of the ul. (See css positioning z-index negative margins for why we can't use z-index for this.)
You can then animate the ul like this:
function scroll() {
var content = 'Hello';
if (Math.random() < 0.3) {
content += '<br/>Another line';
};
$('ul').prepend('<li>'+content)
.css('margin-top',-$('li').first().outerHeight(true))
.delay(1000)
.animate({'margin-top':0},
function() {
$(this).find('li:gt(9)').remove();
scroll();
}
);
};
Calling the scroll function after the animation is finished may be better than using setInterval, because it guarantees that scrolling won't occur until after the previous scroll. The delay keeps it from running continuously.
Fiddle at: http://jsfiddle.net/w0gnzqx6/8/

Choosing Parent Height With white-space:nowrap

So I have these DIVs which I have arranged to slide left an right inside of the parent.
See the following JSFiddle to see the design:
http://jsfiddle.net/StevP/C9WL7/
You can see that by adjusting the margin-left of the first child DIV by multiples of -100%, it's rather simple to correctly horizontally position the DIVs inside the parent. Therefore, it's very easy to animate.
Now, this brings me to my issue. I'm using jQuery to move them left and right. It works great. However, I'd like to choose which child the parent gets its height from.
I know, I can just add...
$('#parent').height($('.child:eq()').outerHeight());
...Which is what I have it currently doing. However, the contents of the children are likely to change causing them to resize (by animate) and, therefore, be cut off. So, having a set height isn't a possibility.
I need to use height:auto; on the parent and somehow cause it to ignore the heights of specific children. I can't for the life of me think of a way.
I don't want to use a timer and onresize/.resize() don't seem to work with my Chrome.
You could use jQuery to monitor the DOM subtree and adjust the height of your parent div in the callback like this:
$('.content').bind('DOMSubtreeModified', function(e) {
if (e.target.innerHTML.length > 0) {
$(".parent").height($(".content").height());
}
});
Here's a working jsfiddle: http://jsfiddle.net/9386d/
And a question explaining the dom subtree: jQuery watch for domElement changes?
jQuery docs for bind(): http://api.jquery.com/bind/
Well... To be perfectly honest I'm not really a huge fan of jQuery anymore so I feel bad offering this answer. It just feels so frik'n inefficient, but here is a solution that does three things: 1) it resizes the hight of the container on step and uses a CSS transition attribute for eye candy (works just as well without). 2) it sets the child height of all but the current child to 0 and uses overflow:hidden so they don't affect the flow of the document anymore. 3) it resets these children to automatic height on animation start so they are visible during transition. All I can say is "yuck", but it does work.
CSS
.child{
...
overflow:hidden;
}
jQuery
var animation_prefs = {
duration: 3000,
start: function() {
$('.child').height('auto');
},
step: function(now) {
var current_index = (Math.floor((now + 50) / 100) * -1);
$('#parent').height($('.child:eq(' + current_index + ')').outerHeight());
$('#parent').data('current', current_index);
},
complete: function() {
$('#parent').height('auto');
$('.child:not(:eq('+$('#parent').data('current')+'))').height(0);
}
}
$('.child:eq(0)').animate(
{
marginLeft:'-200%' //~ Move it back 2 children
},
animation_prefs
).animate(
{
marginLeft:'-100%' //~ Move it back 1 child
},
animation_prefs
).animate(
{
marginLeft:'-200%' //~ Move it back 2 children again
},
animation_prefs
);
Demo
http://jsfiddle.net/Gq4xs/show
Source
http://jsfiddle.net/Gq4xs/

jCarousel with unknown content width

I am trying to use jCarousel plugin for jQuery in order to provide my website users with scrollable (horizontal) content.
The content I am mentioning is basically user defined <li> elements, styled so that they have a feel and look of a tab. so basically I am trying to achieve the same effect of the tabs in pageflakes.com. As you may imagine, the users are creating tabs and providing tab names by themselves..
jCarousel needs you to specify a fixed width for the content e.g., all their examples are based upon images that have fixed height and width. but in my case I have not control over what the user will name his/her tab...making it impossible for me to guess the width of the total container div.
I have tried using a silly method such as guessing the width programatically assuming each letter being approx 5 pixels and multiplying 5 with the length of the word they have given as a name for a tab. even in this case, i needed to manipulate the css file dynamically which I am not sure how to do and even if it is feasable to do so..
Any solutions appreciated...
<lu>
<li class='MyTab' id='578'>This is my tab</li>
<li class='MyTab' id='579'>of which I can</li>
<li class='MyTab' id='580'>never guess</li>
<li class='MyTab' id='581'><img src='img/bullet_key.png' /> The length of</li>
</lu>
The above html is produced programatically through ajax_tabs_output.aspx, loaded into a javascript array and the jCarousel takes care of the rest..
function outputTabsArray() {
$.ajax({
url: 'ajax_tabs_output.aspx',
type: 'get',
data: 'q=array',
async: false,
success: function(out)
{
tabs_array = out;
}
});
}// end outputTabsArray
function mycarousel_itemLoadCallback(carousel, state)
{
for (var i = carousel.first; i <= carousel.last; i++) {
if (carousel.has(i)) {
continue;
}
if (i > tabs_array.length) {
break;
}
carousel.add(i, mycarousel_getItemHTML(tabs_array[i-1]));
}
};
/**
* Item html creation helper.
*/
function mycarousel_getItemHTML(item)
{
return '<div class="MyTab" id="' + item.tab_id + "'>" + item.tab_name + '</div>';
};
$('#mycarousel').jcarousel({
size: tabs_array.length,
itemLoadCallback: {onBeforeAnimation: mycarousel_itemLoadCallback}
});
The closest thing to what you want is probably jscrollhorizontalpane. I've never used it so I can't vouch for it's effectiveness as a solution to this specific problem.
This sort of widget shouldn't be to hard to make if you want to attempt it. I'll break down the approximate method I would use:
Make sure to use plenty of wrappers.
<div class="scrollable">
<div class="window">
<div class="space">
<ul class="tabs">
<li class="tab">...</li>
<li class="tab">...</li>
<li class="tab">...</li>
</ul>
</div>
</div>
←
→
</div>
What we'll be doing is shifting the "space" element back and forth inside "window" element. This can be done by setting position:relative to "window" and position:absolute to "space" and then shift it about using left:-??px, but I'll use the scrollLeft property.
Add some CSS.
.window {
overflow : hidden;
width : 100%;
}
.space {
width : 999em; /* lots of space for tabs */
overflow : hidden; /* clear floats */
}
.tabs {
float : left; /* shrink so we can determine tabs width */
}
.tab {
float : left; /* line tabs up */
}
This is just the basic stuff that the technique needs. Some of this stuff could be applied by jQuery,
Add events to window resize.
$(window)
.resize(function () {
var sz = $('.window');
var ul = sz.find('ul');
if ( sz.width() < ul.width() ) {
$('.scrollable a.left, .scrollable a.right').show();
}
else {
$('.scrollable a.left, .scrollable a.right').hide();
}
})
.trigger('resize');
Add events to scroll buttons.
$('.scrollable a.left').hover(
function (e) {
var sz = $('.window');
sz.animate({ scrollLeft : 0 }, 1000, 'linear');
},
function (e) {
$('.window').stop();
});
$('.scrollable a.right').hover(
function (e) {
var sz = $('.window');
var margin = sz.find('ul').width() - sz.width();
sz.animate({ scrollLeft : margin }, 1000, 'linear');
},
function (e) {
$('.window').stop();
});
Done!
The widths could also be calculated by looping through the "tab" elements and summing upp outerWidth. This is unnecessary if you have full control but might be a better choice for a full standalone plugin.
From what I can tell, you're trying make JCarousel do something it was never designed to do. Based on what I read on the JCarousel website it appears to be an image rotator.
What it sounds like you want is a tabbed interface. See JQuery UI Tabs for a demo and documentation on how to implement it.
If I'm totally wrong and all you're looking for is a tutorial on how to do proper CSS tabs, have a look at:
http://unraveled.com/projects/css_tabs/
Soviut,
You are actually quite right! I am trying to make JCarousel do something it wasn't designed for.
However, I also wouldn't like to use a tab plugin or any similar stuf as I NEED FULL CONTROL over my output. Such as injecting more elements into the tabs when needed such as double clicking, loading and many other etc. etc.
Actually what I am looking for a way to scroll horizontally the content within a div with arrows on the left and right.. To be more precise, I need the exact same structure of the tabs seen in www.pageflakes.com
The user will be able to create tabs by clicking on a link (or any other element for that matter), they will be able to inline edit its name, whenever they have more tabs then the length of the div, the buttons will be visible allowing them to slide inside the div, I will have events bound to their load, click and double click events.
To summarize I have everything ready and working except for the sliding/scrolling part. Its only I have to get help from you guys regarding how to achieve this functionality..
Kind regards,
What you're looking for isn't tabs, but draggable panels. One example can be found here, called JQuery Portlets. The related blog entry about Portlets can be found here.
You may also want to look into JQuery UI's Draggable, Droppable, and Sortable plugins.

Categories