change content on resize without page refresh? - javascript

I'm using ShieldUI's "Tabs" and there is a property for the tabs position. I want when the screen width is >= 768px to display the tabs at "left" and while <= 768px to display them on the "top". I came to here:
var $window = $(window);
var $pane = $('#tabsDiv');
function checkWidth() {
var windowsize = $window.width();
if (windowsize < 768) {
$pane.shieldTabs({
position: "top"
});
} else {
$pane.shieldTabs({
position: "left"
});
}
}
checkWidth();
$(window).resize(checkWidth);
But when i'm full width and i go "mobile" i have to refresh the page to get what i want, is there a way to do that without page refresh?

We kind of have some issues with your code, along with the Shield UI limitations. Let's see some of them:
First of all, for what I've checked at the official docs, the framework does support component refreshing, but only for new options you'd like to add to your widgets. Therefore, your code won't work when you try to update specific features of the tab (like the position). This means that it's good to have a global function (initTabs, in our case) for destroying and recreating the whole tab structure;
Second, once you do that, don't declare the resizing function inside of this global new one, in order to evict recursiveness problems;
It's also a good idea creating a global variable to store the state of the tabs position, since we have now local/global functions to handle.
Check out the final working code: https://jsfiddle.net/ro0a5bhv/4/
Note: See that I had to do a workaround regarding the CSS property min-height, which is always set by the framework when switching tab views.

Related

Simplebar scrollbar plugin - Scroll to top / reset position in function

I need to be able to scroll back to the top of a page within my ui-routed angular one-page site when a function is triggered, but I've used the simplebar scrollbar plugin as a custom scroller, so can't use the window scrolltop method to take the user back to the top of the page.
I can't use any window/document scrolling method as the container that utilises simplebar is a fixed 100vh container, therefore the window is always scrolled to the top.
I've tried using the jquery method below to reset the position of the scrollbar back to the top, but can't get it working, and there are no error messages in the console.
angular.element('#mainContent').simplebar('getScrollElement').scrollTop(0);
I've also tried this in plain js, which returns 'is not a function' in the console:
var mainContent = new SimpleBar(document.getElementById('mainContent'));
mainContent.SimpleBar.getScrollElement().scrollTop = 0;
It seems that the new SimpleBar(xxx) approach does not work with data-simplebar html attribute.
I don't want to initialize the SimpleBar programmatically so I used this in stead:
$('#mainContent .simplebar-content-wrapper').scrollTop(some_value)
The actual scrollable element would have simplebar-content-wrapper class, and it would be inside the element that you've added SimpleBar for.
The class simplebar-content-wrapper was mentioned in its documentation and can be expected to be consistent across versions.
There would be problem if you have cascaded SimpleBars. Solutions:
$('#mainContent .simplebar-content-wrapper')[0].scrollTop = some_value: This would only scroll the correct SimpleBar because the elements returned by nowaday JQuery is in document order.
$('#mainContent>>>>.simplebar-content-wrapper').scrollTop(some_value): The hierarchy of SimpleBar components is not guaranteed to be unchanged in future versions and this may fail in the future.
I have found the solution. You can access scroll element:
const el = new SimpleBar(document.getElementById('layout'));
el.scrollContentEl.scrollTop = 0;
You can try .animate for this:
$("body").animate({ scrollTop: 0 }, "slow");
var el = new SimpleBar(document.getElementById('myElement'));
el.getScrollElement().scrollTop = 0;

Screen width detection with Javascript / jQuery - Sidebar to Tabbed Pullout

I have a responsive design, with selectable sidebar data drawn from a database. At a screen width less than 751 pixels, that sidebar becomes a pull-out tab on the left of the screen. I cannot feasibly reload the data on state change (sidebar to tab or vice versa) as the amount of data is extensive. So, the solution seems to be using the tabbed state (using MB Extruder - a "hidden" tab utility) as the sidebar, also, and just changing the state of the div. However, that cannot be done without javascript as, in the sidebar state, Extruder needs to be open, whereas it needs to be closed when in the tab state.
So, I am doing the following to set the sidebar/tab:
$(document).ready(function()
{
CheckScreen();
},
$(window).resize(function()
{
CheckScreen();
}));
function CheckScreen()
{
var ww=$(window).width();
if(ww < 751)
{
$('#extruderLeft').closeMbExtruder();
$('.extruder.left .flap').css('display', 'block'); // The tab
$('.site_wrapper').css('padding-left', '30px');
}
else
{
$('#extruderLeft').openMbExtruder(true);
$('.extruder.left .flap').css('display', 'none');
$('.site_wrapper').css('padding-left', '0px');
}
}
This changes the state from a sidebar column to a hidden state with a small tab on the left side of the screen when the screen width is less that 751 pixels. This will work fine at any size screen on document.ready. It will adjust fine when dragging the side of a browser from larger to smaller. However, when dragging back out to a larger width, the div will, rather randomly, switch from one state to another.
Perhaps there is a better way to do this altogether. If worse came to worse, I could have two separate entities (sidebar and tabbed state) holding the same data, and just use CSS, but that would be ridiculously redundant.
The problem seemed to be faulty conditions in the if-clauses (see comments under the question).
This should do the trick:
$(window).load(function() {
$(window).resize(function() {
if ($(window).width() < 751) {
if ($('.extruder.left .flap').css('display') != 'block') {
$('#extruderLeft').closeMbExtruder();
$('.extruder.left .flap').css('display','block'); // The tab
$('.site_wrapper').css('padding-left','30px');
}
} else if ($('.extruder.left .flap').css('display') != 'none') {
$('#extruderLeft').openMbExtruder(true);
$('.extruder.left .flap').css('display','none');
$('.site_wrapper').css('padding-left','0');
}
}).resize();
});
Notice the extra if-clauses checking the display-state:
if ($('.extruder.left .flap').css('display') != 'block') {
and
} else if ($('.extruder.left .flap').css('display') != 'none') {
This makes sure the sidebar/tab-switch only occurs on the break point of the specified screen-width, and the if-clauses aren't unnecessarily executed.
I also changed your script a bit to make more efficient use of jQuery. This way you don't have to create a named function AND call it twice. (I always put window.resize inside window.load instead of document.ready because if you need to scale things that only works properly after load anyway, but for your purpose both will work.)

Maintaining page view on window resize in a responsive website

Situation:
Suppose we are reading the content somewhere down the page that is built to be responsive. Suppose also that we resize the browser window to a smaller size and that some content above get extended down due to the thinner width, hence making the whole page longer. Then as we resize, whatever content we are looking at will get pushed down the page accordingly.
Example:
Suppose we were to look at the Helper classes section in this page. Then shrinking/expanding the window a sufficient amount moves the bit we were reading down/up the current view.
Prompt:
Is there any way we can fix this? I.e. maintain our current view of the page regardless of what happens to the contents above it when we resize the window.
Thoughts:
I am thinking that we could at least start with javascript and put an event on window resize. Then automatically scroll the page to the top-most element that was in our view on event fire. I don't know how this will affect the performance, however, especially in bigger pages.
There's also the problem of refering to the top-most element in current view. The top of our current view might be cutting off the top portion of some elements, not to mention that there's usually more than 1 element layered on top of one another at any point within the page. The notion of top-most element I've mentioned is not very well-defined :(
Also rather than a problem of responsive design in general, instead it seems to me like this is a problem with the default scrolling behaviour of web browsers? Or perhaps I am missing some circumstances where the current behaviour is desirable.
Edit 2 4
Updated fiddle (see fullscreen result) based on Rick Hitchcock's solution's solution.
With jQuery:
//onresize:
var scrollAmount;
if (topNode.getBoundingClientRect().top >= 0) {
scrollAmount = $(topNode).offset().top - topNode.getBoundingClientRect().top;
} else {
scrollAmount = $(topNode.offset().bottom - topNode.getBoundingClientRect().bottom;
}
$(window).scrollTop(scrollAmount);
The fiddle is acting a bit weird even in the same browsers, I've uploaded the same script using a free hosting here.
Still need to incorporate the IE, Opera and Safari fix for elementFromPoint.
Edit 3
Thanks for all the help, Rick Hitchcock. Welcome to stackoverflow, by the way :)
The discussion is turning into cross-browser compatibility issues so I've accepted your answer since we've pretty much got the answer to the original question. I'll still be fixing up my implementation though. The focus being cross-browser issues, topNode criteria, and topNode cut-off handling.
An edge case
While playing around with it, I noticed that when we were at the bottom of the page in a small viewport, then switch to a larger viewport (let us assume now that some more elements that were originally above the element we saw now came into view due to shorter container from wider viewport) the window cannot always lock the topNode to the top of the viewport in such a case since we've reached the scroll bottom. But then switching back to the small viewport now uses a new topNode that got into the viewport during the switch.
Although this should be expected from the behaviour being implemented, it is still a weird side-effect on scroll bottom.
I will also be looking into this in due course. Initially, I am thinking of simply adding a check for scroll bottom before we update topNode. I.e. to keep the old topNode when we've reached scroll bottom until we've scrolled up again. Not sure how this will turn out yet. I'll make sure to see how Opera handle this as well.
Here's what I've come up with:
(function(){
var topNode;
window.onscroll=function() {
var timer;
(function(){
clearTimeout(timer);
timer= setTimeout(
function() {
var testNode;
topNode= null;
for(var x = 0 ; x < document.body.offsetWidth ; x++) {
testNode= document.elementFromPoint(x,2);
if(!topNode || testNode.offsetTop>topNode.offsetTop) {
topNode = testNode;
}
}
},
100
)
}
)();
}
window.onresize=function() {
var timer;
(function(){
clearTimeout(timer);
if(topNode) {
timer= setTimeout(function(){topNode.scrollIntoView(true)},10);
}
}
)();
}
}
)();
If there were a window.onbeforeresize() function, this would be more straightforward.
Note that this doesn't take into account the scrolled position of the element's textNode. We could handle that if only the height of the window were resized. But resizing the width would generally cause reformatting.
This works in Chrome, Firefox, IE, and Safari.
Edit
How it works
The code's closures make variables private, and the timers prevent the code from running constantly during scrolling/resizing. But both tend to obfuscate the code, so here's another version, which may aid in understanding. Note that the onscroll timer is required in IE, because elementFromPoint returns null when it used in onscroll event.
var topNode;
window.onscroll=function() {
setTimeout(
function() {
var testNode;
topNode= null;
for(var x = 0 ; x < document.body.offsetWidth ; x++) {
testNode= document.elementFromPoint(x,2);
if(!topNode || testNode.offsetTop>topNode.offsetTop) {
topNode = testNode;
}
}
},
100
)
}
window.onresize=function() {
if(topNode) {
topNode.scrollIntoView(true)
}
}
topNode maintains the screen's top-most element as the window scrolls.
The function scans the screen left to right, along the 3rd row: document.elementFromPoint(x,2)*
It doesn't scan along the 1st row, because when IE does scrollIntoView, it pushes the element down a couple pixels, making the top-most screen element the previous element. (Figured this out through trial and error.)
When the window is resized, it simply positions topNode at the top of the screen.
[*Originally, onscroll scanned left to right along the 11th row (in pixels) until it found an element with just one child. The child would often be a textNode, but that wouldn't always be the case. Example:
<div><ul><li>...<li>...<li>...</ul></div>
The div has only one child – the ul. If the window were scrolled to the 50th li, scanning left to right would incorrectly return the div due to the inherent padding of lis.
The original code has been updated.
]

Perform function when window.width is resized but not the height

I'm developing a responsive website and I have some tabs that change from tabs to an accordion on small screens. Here is how I am doing it at the moment:
var myGlobalVariable = {};
$(window).resize(function(e) {
duringResize();
myGlobalVariable.windowWidth = window.innerWidth;
});
function widthHasChanged() {
return window.innerWidth !== myGlobalVariable.windowWidth;
}
function duringResize() {
if(widthHasChanged()) {
if(Modernizr.mq('all and (min-width:1000px)')) {
/* Do stuff for tabs */
} else {
/* Do stuff for accordion */
}
}
}
I'm not happy about this because I'm having to use a global variable to store the last width of the browser window in order to check wether the width has changed.
The reason I have to do this is because on mobiles when the tabs are in accordion mode clicking on one actually makes the document taller to accommodate the tab content. For some reason this is classed as a resize even though the 'window' is still the same size on a mobile. This meant that my tab/accordion code was being called even when the width hadn't changed and this was messing things up.
Is there something I'm missing or is this the only way to achieve this? jQuery and vanilla javascript solutions are welcome.
There is no specific event to bind to for window width change. Your solution looks fine to me.
The only other way I could think of was to do a regular check on $(window).width inside a timer. I think on balance, the single global variable is preferable :)

Why would images in the Mootools Multibox have scrollbars the FIRST time they are shown?

We are using Mootools Multibox to display images.
The first time we view it with Chrome and Safari, the images are zoomed in and have scrollbars.
When we reload the page, the images display correctly without scrollbars.
What could be the cause of this?
How can we fix this so that the images are displayed with their correct sizes the first time viewed in Chrome and Safari?
in this block of code:
showContent: function(){
this.box.removeClass('MultiBoxLoading');
this.removeContent();
this.contentContainer = new Element('div', {
'id': 'MultiBoxContentContainer',
'styles': {
opacity: 0,
width: this.contentObj.width,
height: (Number(this.contentObj.height)+this.contentObj.xH)
}
}).inject(this.box,'inside');
it sets with width of the content box to the contentObj.width direct. which is fine if the browser has the image in the cache - at which point it will work but not so fine when it does not.
it uses Asset.js to load an image here:
load: function(element){
this.box.addClass('MultiBoxLoading');
this.getContent(element);
if(this.type == 'image'){
var xH = this.contentObj.xH;
this.contentObj = new Asset.image(element.href,{onload:this.resize.bind(this)});
this.contentObj.xH = xH;
}else{
this.resize();
};
},
the problem is, only after the onload fires does the browser know the actual width and height of the image (available through this.width / this.height if not bonund to class scope). although this will return an image object early (into contentObj), it probably shouldn't just yet and should do it after the onload fires. the onload here should be what injects the image into the container and sets width and height to host it. instead, it applies this.resize(image)
i hope this gives you some ideas as to how to refactor the class to make it work better.
ADDITONALLY: var xH = this.contentObj.xH; and this.contentObj.xH = xH; -> element storage for other elements direct into the object? this pre-dates mootools 1.2 which introduced closure based uid specific storage per element. bad practice, can cause slowness in IE, memory leaks etc.
refactor to this.contentObj.store("xH", something) with this.contentObj.retrieve("xH") to get it back

Categories