I am using DataTables for a large quantity of data gathered from various ASP databases.
In addition, I am using the following DataTables plug-ins:
FixedHeaders, FixedColumns, ColReorder.
Now, with the ColReorder function, when a header (a column actually) is dragged to reorder it and is overflowing to the sides (horizontal scrolling), I would like the table to scroll to the side with it, so that the column can be dragged not only to the viewed area of the table but to the entire table.
I have tried nomerous ways and walkarounds such as trying to make it scroll when the curser is close to the boundaries (like here in "Drag Scrolls" http://javascriptmvc.com/docs.html#!jQuery.Drag), but I did not succeed, as well as this method - http://mootools.net/docs/more/Interface/Scroller.
I would highly appreciate your help.
Thank you all in advance.
Here is an approach that may get you closer to solving it. Based on my experiments, you can copy and modify dataTables.colReorder.js in function _fnMouseMove just before this.dom.drag.css(...) code.
var scrollLeftVal = $(this.s.dt.nTableWrapper).find('.dataTables_scrollBody').scrollLeft();
if((e.pageX - this.s.mouse.offsetX) > (this.s.dt.nTableWrapper.clientWidth - 50)) {
scrollLeftVal += 10;
$(this.s.dt.nTableWrapper).find('.dataTables_scrollBody').scrollLeft(scrollLeftVal);
}
This will scroll the window to the right when dragging a column header. A reverse method would be needed as well, as well as modifying the positions of the pointer and drag elements, such as below:
this.dom.pointer.css( 'left', this.s.aoTargets[i-1].x - scrollLeftVal );
If you decided on this approach, you would be better off long term to build an extension/plugin rather than modifying core code. This is just for experimental purposes.
I started down this path, but believe I'll do my own reordering interface since this approach, even if working and scrolling properly, is not as ideal for the project I'm working on.
Hope this helps someone get closer to a solution!
Ryan
Tested with bootstrap https://jsfiddle.net/bababalcksheep/gsf2r1v4/17/
I added this code by modifying dataTables.colReorder.js in function _fnMouseMove just before this.dom.drag.css(...)
Seems to work Fine however with only one problem.
Lengthy columns have hard time snapping to next short width column.
For Example: in https://jsfiddle.net/bababalcksheep/gsf2r1v4/17/ , try dragging project column to next EXT column. I suspect this is because project column has larger width.
Any further fix to this problem will be much appreciated
var scrollHead = $(this.s.dt.nTableWrapper).find('.dataTables_scrollHead');
var scrollBody = $(this.s.dt.nTableWrapper).find('.dataTables_scrollBody');
var scrollLeftVal = e.pageX - scrollHead.parent().offset().left - this.s.mouse.offsetX;
//
var difference = scrollHead[0].clientWidth - scrollLeftVal ;
//is near Right edge, scroll to far right
if (difference < 150){
scrollLeftVal = scrollHead[0].scrollWidth - scrollHead[0].clientWidth;// max scrollleft Value
}
//is near Left edge, scroll to far Left
if(scrollLeftVal < 100){
scrollLeftVal= 0;
}
//
scrollBody.scrollLeft(scrollLeftVal);
Related
In Jquery I'm aware you can move the scrollbars' starting location. Is this possible with pure javascript? To clarify, when the user loads the page I simply want the horizontal scrollbar to start scrolled all the way to the right, instead of starting at the left. If there are cross-browser issues, I'm particularly concerned with this working in Chrome.
document.body.scrollLeft = ( divRef.scrollWidth - divRef.offsetWidth ) / 2 ;
NOTE:
This can give odd results with IE8 and earlier.
I've made an example with a div, you can easily adjust this to your body tag, or another div, please see the demo.
var a = document.getElementById('body');
console.log(a.clientWidth)
function moveWin(a) {
window.scrollTo(a.clientWidth,0);
}
moveWin(a)
DEMO
SIDENOTE:
To select the body, simply use
var a = document.getElementsByTagName('body')[0]
I created a dynamic table that scrolls left and right, has resizable columns, has a fixed header, etc. This table works great on EVERY browser I've tried. Even IE8 looks good (missing features, but still good).
This issue arises when I try to view the table in Safari 7.0.4 on my Macbook.
Attached is what is should look like (the fixed header is on the bottom for demonstration purposes):
when you scroll, the fixed header, body, and fixed scrollbar all are connected via some jQuery scrollLeft() functions (scroll one, scroll all):
var tableHeaderSpace = $('.table-full-wrap-space'),
tableHeader = $('.table-full-wrap-header'),
tableBody = $('.table-full-wrap-body'),
tableScroll = $('.table-full-wrap-scroll');
tableScroll.bind('scroll', function() {
tableHeader.scrollLeft(tableScroll.scrollLeft());
tableBody.scrollLeft(tableScroll.scrollLeft());
});
tableHeader.bind('scroll', function() {
tableScroll.scrollLeft(tableHeader.scrollLeft());
tableBody.scrollLeft(tableHeader.scrollLeft());
});
tableBody.bind('scroll', function() {
tableScroll.scrollLeft(tableBody.scrollLeft());
tableHeader.scrollLeft(tableBody.scrollLeft());
});
$(window).bind("scroll", function() {
var tableHeaderOffset = tableHeaderSpace.offset().top;
if (this.pageYOffset >= tableHeaderOffset) {
tableHeader.addClass('isFixed');
} else {
tableHeader.removeClass('isFixed');
}
});
Again, this works great...but as you scroll right a bit more, the browser starts duplicating content within that fixed header:
The issue is is that no 'actual' content is being duplicated - this is some sort of browser fragmenting that is showing duplicates - without adding elements in the DOM.
The next picture is the browser doing some more "magic". at certain points in horizontal scrolling, the whole fixed header's colors gets inverted:
I wasn't able to get a snapshot of it, but it also once duplicated the "record count" bar below it.
Anyone have any ideas what's going on here? I tried to duplicate this in jsFiddle but no dice. From that, I would assume that this is an issue with my code, but the results are only with ONE specific browser on mac (safari), and it is doing some STRANGE stuff.
Last note - since I can't replicate this in jsFiddle, i'm not sure how I could report this to Apple (the working (or 'broken') example is proprietary and I can't give out access to it).
EDIT:
here's the jsfiddle where I tried to duplicate the issue (very rough - but it's functional):
jsFiddle Duplication Attempt
so - I knew this wouldn't be a hot topic question, but I thought I would still give it a go ahead.
as for the answer, I found some old table css that was overlapping my new stuff - which in turn was somehow flipping safari out so bad that it was fragmenting it.
previous old code: background: transparent;
new code: background: #fff;
This doesn't make sense to me - but until someone else comes up with an hypothesis, I'll mark this as the answer.
now my number-one contender for worst browser: safari - look out, IE.
Hello StackOverflow Community,
what I am trying to achieve is a header that can be moved with the mouse.
You klick into the header and drag the mouse and the elements inside the header will move with different speeds.
I achieved the parallaxing part but the performance is not really good. It is partially a bit laggy while dragging the backgrounds.
My question now is: what can be changed in the code to get a performance boost?
That's the part of the code that takes care of parallaxing. On every mousemove a each loop is executed which I think is the reason for the performance beeing so laggy:
var dragging = false;
var clickMouseX;
//Our object for the layers
//each layer has a different scrolling speed
var movingObjects = {
'#header-l1' : {'speed': 1},
'#header-l2' : {'speed': 1.4},
'#header-l3' : {'speed': 1.85},
'#header-l4' : {'speed': 2.2},
};
$('#header-wrapper').mousedown(function(e){
dragging = true;
//Get initial mouse position when clicked
clickMouseX = e.pageX;
$(this).mousemove(function(mme){
//execute only if mousedown
if(dragging){
//iterate through all layers which have to be parallaxed
$.each(movingObjects, function(el, opt){
var element = $(el);
//get difference of initial mouse position and current mouse position
var diff = clickMouseX - mme.pageX;
//scroll-position left speed 1
if(diff < 0) diff = -1;
//scroll position right speed 1
if(diff >= 0) diff = 1;
//get current position of layer
currLeft = parseInt(element.css('left'));
//get current layer width
elWidth = element.width();
//if right border is reached don't scroll further
if(currLeft < -(elWidth - 810)){
element.css('left', -(elWidth - 810));
}
//so do with left border
if(currLeft > 0){
element.css('left', 0);
}
//parallax it! Subtract the scroll position speed multiplied by the speed of the desired
//layer from the current left property
element.css('left', parseInt(element.css('left')) - diff*opt.speed);
});
}
});
/* Cursor */
$(this).css('cursor', 'pointer');
return false;
});
I also put a fiddle up:
http://jsfiddle.net/yWGDz/
Thanks in advance,
Thomas
P.S. maybe someone even finds out why layer two and three have the same scroll speed while having different speeds defined.
I worked at this a bit, and came up with this: http://jsfiddle.net/amqER/2/
This works a lot faster than the original (especially in firefox, where it performs a whole lot better, chrome it's still pretty slow). I also changed up some of the logic in your code, to make it make more sense.
A list of things that I did:
Minify your pngs
2 of your png files were over 2 megs, so I threw them into a png compressor (tinypng) and that reduced the size a lot. This helps with loading time and overall snappiness.
Re-use values as much as possible
In your original code, you wrote to and then subsequently read from the css left property a couple times in your code. Doing this is going to make it a lot slower. Instead, I kept an left property, and would only touch $.css when I absolutely needed to. Likewise for reading each element's width each update.
Also, like I said, I modified your logic to (I think) make more sense, given what you were trying to accomplish. It calculates a new diff each update, and tries to move according to that. Also, it doesn't try to keep moving once one of the images falls off (which yours does if you move all the way to the right, and it looks really weird). You can also look at this: http://jsfiddle.net/amqER/5/, which maybe is more like the control scheme you wanted.
Just some quick performance tips.
Try not to use $(this).mousemove instead save $(this) into a variable and use that.
var th = $(this);
th.mousemove...
Try to avoid using $.each. This is probably the part that's slowing your code down.
You can replace it with a for loop, but I would suggest, in this case, sending in each element one by one.
var parallax = function(img){
};
parallax(img1);
parallax(img2);
instantly-increase-your-jquery-performance
Whilst Xymostech's answer does greatly improve upon the original poster's original code; the performance is hardly improved for me in Chrome.
Whilst inspecting the page FPS, the solution posted here runs at 15FPS for me on a Retina MacBook Pro.
I made a very simple change to the code, altering it to use translate3d properties instead of left. Now, it runs at 55-60 FPS for me. I'd call that a massive performance boost.
If 'show paint rectangles' are turned on in Chrome, you'll see the previously posted solution is continually painting changes to the dom whilst the parallax is in motion. With the translate3d solution, there's simply zero painting done the whole time the parallax is in motion.
http://jsfiddle.net/LG47e/
I am looking for help / a point in the right direction / or a solution for a flicker/jump, when scrolling on a looping/infinite website, which can be seen in this fiddle.
What seems to be causing the jump is:
"$(window).scrollTop(half_way - child_height);", and what could also be a Chrome windows scrollTop bug, but it is happening in all browsers at the moment.
If I remove "- child_height" there is no longer a flicker but the page no longer scrolls correctly, which can be seen in this fiddle.
Also, on the very first scroll the right hand column jumps up by three boxes - also because of 'half_way', which I can fix by giving it a "bottom: -600px;"
The full code:
http://jsfiddle.net/djsbaker/j3d8r/1/
var num_children = $('#up-left').children().length;
var child_height = $('#up-left').height() / num_children;
var half_way = num_children * child_height / 2;
$(window).scrollTop(half_way);
function crisscross() {
$('#up-left').css('bottom', '-' + window.scrollY + 'px');
$('#down-right').css('bottom', '-' + window.scrollY + 'px');
var firstLeft = $('#up-left').children().first();
var lastLeft = $('#up-left').children().last();
var lastRight = $('#down-right').children().last();
var firstRight = $('#down-right').children().first();
if (window.scrollY > half_way ) {
$(window).scrollTop(half_way - child_height);
lastRight.appendTo('#up-left');
firstLeft.prependTo('#down-right');
} else if (window.scrollY < half_way - child_height) {
$(window).scrollTop(half_way);
lastLeft.appendTo('#down-right');
firstRight.prependTo('#up-left');
}
}
$(window).scroll(crisscross);
Okay - here is a 'working' version - and by works I mean it less flickery than before. I thought it was flicker free, and it was when I was on battery power, but plugged into the mains and the CPU is fast enough to get flicker.
As I mentioned, to get rid of the flicker you need to clone the objects, manipulate them and then replace them into the DOM, rather than just manipulating the DOM directly.
I did this by getting the contents of <div id="content"> manipulating them and then replacing them into that <div>.
Also, it's a good idea to only find things in the DOM once, and from then on use a reference to that object rather than searching repeatedly. e.g.
var leftSide = $(clone).find('.up-left');
....
lastRight.appendTo(leftSide);
....
$(leftSide).css('bottom', '-' + window.scrollY + 'px');
rather than:
lastRight.appendTo('#up-left');
$('#up-left').css('bottom', '-' + window.scrollY + 'px');
Searching the DOM is relatively slow, and so storing references can improve performance/reduce flicker.
Storing the object also makes the code easier to understand (imho) as you can easily see that you're referencing the same thing, rather than possibly different things.
I still get flickering in chrome on windows with Danack solution. For this site I would control all the scrolling (you already scroll manually one of the sides), and give elements absolute positions.
Or if you insist on using the browser scrolling, may be use animations: animate the height of the last elements till 0px then use appendTo, and then animato from 0px to the normal height...
This might be a long shot, but I had the same flickering when working with infinitescroll,
and ended up using imagesLoaded.I ended up appending the additional images (now loaded) with a fade in, and that prevented them from flickering because of the fact they were loaded.
So maybe by using the imagesloaded - or a callback on the images, you can solve the flickering. It does decrease the speed though. I can image that if you want to scroll through everything as fast as possible, this might not be the solution. Good luck!
A solution would be to not use the native scrolling functionality but to simulate scrolling. This would be done by setting the overflow of your content to "hidden" in addition with capturing the "mousewheel" event on it and triggering some action when it is called. I started to try this out here (using MooTools instead of jQuery since I'm more fimilar with it). It's currently just "working" on the left side by altering the margin-top of the first element.
My next steps would be:
Check if the negative margin-top of the first element is bigger than the height of it and move it to the right side if so.
Same logic for the last box on the right side with a negative margin-bottom.
This has some downsides, though. Simulating scrolling doesn't feel as natural as the native scrolling functionality and clicking the mousewheel doesn't work. These might be solveable but it would require some more coding to get it to work smoothly. Anyway, in the end you would have a solution without any flickering and with no sticky scrollbar at the side (An idea for a replacement could be a small area on the side that triggers the scrolling on mouseover).
I want to provide the user with the experience of scrolling through content, but I would like to load the content dynamically so the content in their viewing area is what they would expect, but there is no data above or below what they are looking at. For performance reasons, I don't want that data loaded. So when they scroll down new data gets loaded into their view, and data previously in their view is discarded. Likewise when scrolling up. The scroll bar should represent their location within the entire content though, so using "infinite scrolling" or "lazy loading" does not look like what I need.
My solution may be that I need to re-architect things. As of now, my project is a hex-viewer that allows you to drop a binary file onto it. I create html elements for every byte. This causes performance issues when you end up with a 1MB file (1,000,000+ DOM elements). One solution would be to not use DOM elements/byte but I think this will make other features harder, so I'd like to just not display as many DOM elements at once.
Make a div, set overflow to scroll or auto. As user scrolls you can change the content of the div.
You could look at yahoo mail (the JavaScript based one) to see how they do it (they add rows with email as you scroll).
You don't necessarily need custom scroll bars.
You could look for some code here for custom scroll bars:
http://www.java2s.com/Code/JavaScript/GUI-Components/Scrolltextwithcustomscollbar.htm
or here:
http://www.dyn-web.com/code/scroll/
I'm looking for an answer to this question as well so I'll share where I'm at with it.
I have a large amount of content I want to display vertically and have the user scroll through it. I can load it all into the DOM and scroll normally but that initial creation phase is horribly slow and scrolling can awfully slow also. Also, I will dynamically add to it as I stream more data in.
So I want the same thing which is to be able to dynamically populate and update a non-scrolling area with content. I want to make it seem as if the user is scrolling through that content and have a model (which has lots of data) that is kept off the DOM until it would be seen.
I figure I'll use a queue concept for managing the visible DOM elements. I'd store queueHeadIndex and queueTailIndex to remember what off-DOM elements are shown in the DOM. When the user scrolls down, I'd work out what whether the head of queue falls off the screen and if it does update queueHeadIndex and remove it's DOM element. Secondly I'd then work out whether I need to update queueTailIndex and add a new element to the DOM. For the elements currently in the DOM I'd need to move them (not sure if they need animation here or not yet).
UPDATE:
I've found this which seems to have some promise http://jsfiddle.net/GdsEa/
My current thinking is that there are two parts to the problem.
Firstly, I think I want to disable scrolling and have some sort of virtual scrolling. I've just started looking at http://www.everyday3d.com/blog/index.php/2014/08/18/smooth-scrolling-with-virtualscroll/ for this. This would capture all the events and enable me to programmatically adjust what's currently visible etc. but the browser wouldn't actually be scrolling anything. This seems to provide mouse wheel driven scrolling.
Secondly, I think I need to display a scroll bar. I've had a look at http://codepen.io/chriscoyier/pen/gzBsA and I'm searching around more for something that looks more native. I just want it to visually display where the scroll is and allow the user to adjust the scroll position by dragging the scroller.
Stackoverflow is insisting I paste code so here is some code from that codepen link above
var elem = document.getElementById('scroll-area'),
track = elem.children[1],
thumb = track.children[0],
height = parseInt(elem.offsetHeight, 10),
cntHeight = parseInt(elem.children[0].offsetHeight, 10),
trcHeight = parseInt(track.offsetHeight, 10),
distance = cntHeight - height,
mean = 50, // For multiplier (go faster or slower)
current = 0;
elem.children[0].style.top = current + "px";
thumb.style.height = Math.round(trcHeight * height / cntHeight) + 'px';
var doScroll = function (e) {
// cross-browser wheel delta
e = window.event || e;
var delta = Math.max(-1, Math.min(1, (e.wheelDelta || -e.detail)));
// (1 = scroll-up, -1 = scroll-down)
if ((delta == -1 && current * mean >= -distance) || (delta == 1 && current * mean < 0)) {
current = current + delta;
}
// Move element up or down by updating the `top` value
elem.children[0].style.top = (current * mean) + 'px';
thumb.style.top = 0 - Math.round(trcHeight * (current * mean) / cntHeight) + 'px';
e.preventDefault();
};
if (elem.addEventListener) {
elem.addEventListener("mousewheel", doScroll, false);
elem.addEventListener("DOMMouseScroll", doScroll, false);
} else {
elem.attachEvent("onmousewheel", doScroll);
}
I imagine I'll have one class that listens to scroll events by either the virtual scroll method or the ui and then updates the ui scroller and the ui of the content I'm managing.
Anyway, I'll update this if I find anything more useful.
I think avoiding using DOM elements/byte is going to be the easier solution for me than creating a fake scrolling experience.
UPDATE: I ultimately solved this as explained here: Javascript "infinite" scrolling for finite content?
You're taking about using some serious javascript, specifically AJAX and JSON type elements. There is no easy answer to your questions. You'd need to do a lot of R&D on the subject.