Imagine a responsive menu where the the elements are set to float. They're all, say, 150px wide. As the menu is reduced in width, individual elements will, one by one, get bumped down to the next row.
You're trying to determine whether an element has been bumped down. My thought was to design things in such a way where I could find out if the top of the floated element!= 0. If not, it's been bumped down to the next row. I could also evaluate element heights to figure out if an element is in row 2, 3, etc.
Is there a simpler way to determine if a floated element has been wrapped/bumped down due to parent width constraints?
I would try to find offset of element f.e. this way:
$(window).on('resize', function () {
$(".some-menu-item-selector").each(function(e) {
elementOffset = $(this).offset().top,
$(this).html(elementOffset);
});
});
Now when I have offsets I can group elements by "rows". There is only one assumption: elements have same height. If not, thing gets complicated.
Since your elements are a fixed size, 150px, you can simply use some math to figure out where they are at in the container element.
floor(container width / element width) = number of elements per line
element index % element per line = column
floor(element index / elements per line) = row
Simply add an event listener for the containers resize event and do your check there.
This might not be the best way to do it, but it is one possibility using javascript or jQuery.
You can get the position from the top of the first menu item, then check the distance for the next elements. if they are different, you would know that the the element has been displaced.
You would have to do this on load and on window change.
Good Luck!
I know very little JS but you can check height. I'm using a menu for an example to addClass or removeClass.
DEMO: https://jsbin.com/gapaqe/1/
jQuery:
$(window).on("resize", function() {
if ($('nav').height() >= 62) {
$('nav').addClass('test');
} else {
$('nav').removeClass('test');
}
}).resize();
CSS
nav a {
float: left;
padding: 20px;
border: 1px solid red;
}
.test a {
background: blue
}
/* to clear the floats all fast like dude */
nav {
display: table;
}
HTML
<nav>
Link 1
Link 2
Link 3
Link 4
Link 5
Link 6
Link 7
Link 8
Link 9
</nav>
Related
I have a page where I display a long list of results from a DB query.. and I also show a Google Map to the RIGHT of this long list.
Map is roughly 240px wide and maybe 600px long/height.
This MAP is inside a container DIV (#mapContainer).. that contains the map, and a dropdown box above the map canvas.
Currently, the mapContainer scrolls along with the page itself.. what I would like to do is have it be static/fixed element. So it starts/displays/is placed where I have it currently on the page.... if I scroll the page.. the map should stay fixed.. until the end (bottom) of the results are scrolled to..
(I dont want the mapContainer to scroll and cover the footer element/div)
Following this tutorial:
http://www.webgeekly.com/tutorials/jquery/a-simple-guide-to-making-a-div-static-as-you-scroll-past-it/
It doesnt stay fixed..
//sticky map placement
$(function () {
var msie6 = $.browser == 'msie' && $.browser.version < 7;
if (!msie6) {
console.log("NOT IE 6");
var top = $('#mapContainer').offset().top;
$(window).scroll(function (event) {
console.log("scrolling.......");
var y = $(this).scrollTop();
if (y >= top) {
$('#mapContainer').addClass('fixed');
console.log("class added");
}else {
$('#mapContainer').removeClass('fixed');
console.log("class removed");
}
});
}
});
The first console.log() outputs fine.. but nothign in the window.scroll() portion fires ever.
Rest of code used:
#mapContainer{
display:table;
width:240px;
float:right;
/* sticky map */
position: absolute;
top: 458px;
left: 50%;
/* width: 100px; */
margin-left: 339px;
}
#<span class="skimlinks-unlinked">mapContainer.fixed</span> {
position: fixed;
}
On the tutorials page itself.. he has a toolbar on the left side..
that stops 'being fixed' when you scroll all the way to the top.. (it will start to move with the rest of the page scroll at a certain point).. and it doesnt go all the way down to cover the footer either.
I'm not clear why the jQuery portion isnt firing.. and I'm not clear what that last style is for? (seems odd looking)
All this absolute, fixed, relative, to parent, to viewport..etc.. is confusing.
Any easy to read/follow/understand tutorials that will get me to where I want to be? Or suggestions on what I am doing wrong with the correct approach?
I looked at your Fiddle and noticed a couple things:
Your "fixed" class was not represented in the CSS. When I looked into the CSS I saw a span element wrapping a ".fixed" reference with a position property set.
You are styling the mapContainer div using the ID. This is a very rigid selector as the order of CSS selectors goes. The hierarchy of CSS selectors is specifid and IDs will override types and classes. See: http://htmlhelp.com/reference/css/structure.html
The when scrolling, I am seeing the console logging in my dev tools. Also, when inspecting the element, I am seeing it add and remove the class name.
Based on my observations, modifying the CSS selector for your container should do the trick. Adding the ID to the class will keep the CSS rule specific enough:
#mapContainer.fixed { position: fixed; }
Refer to this updated Fiddle for an example with these changes in place:
http://jsfiddle.net/pmurst8e/4/
Update: For demonstration purposes of what I was referring to with the resize I modified your example a bit. It isn't the prettiest, but it conveys the point: http://jsfiddle.net/pmurst8e/6/
Update: There are a couple issues with the latest Fiddle (v12):
The sidebar will always go fixed the moment you scroll because "top" is never calculated. It's being set to zero, and the offset calculation is commented out.
Absolute positioning is relative to the closest positioned parent. If no parent is positioned, it's relative to the window. To constrain an absolute positioned element, set the constraining parent to "position:relative;".
Instead of using a percentage and left position rule, consider positioning the sidebar to the right, relative to the "contentContainer", by a set number of pixels.
When the fixed position takes effect, we also need to set the sidebar fixed left position. Otherwise, it will use the positioning in the CSS. In contrast to absolute positioning, Fixed positioning is relative to the window, meaning an absolute element "right: 10px" will be 10px from the right of the positioned parent, but will appear 10px from the right of the window when fixed.
You don't need a float when you have absolute positioning. Absolute position removes an element from the normal flow of the document, and because of this float does not apply.
I updated the Fiddle to show how to make these adjustments. I cleaned out the float and margin from the mapContainer and left the absolute positioning. With that I set the contentContainer to relative to constrain mapContainer to it. You will also see, on the script side, I added a line to set the offset of mapContainer. Without this, when it becomes fixed it will be 10px off the right border of the window.
Updated Fiddle: http://jsfiddle.net/pmurst8e/14/
Also, you want to leave your top offset line in tact. Without that, it goes fixed the moment the scroll moves and never goes back. When that becomes the case, you're better off just setting fixed permanently.
var top = $('#mapContainer').offset().top; // you want this
Regarding the bottom boundary, you can do a couple things:
Resize the sidebar so that it shrinks to the window size. This is demoed in my example from my first post in this and the downside is it forces the sidebar to become a scrollable div so the child content is all visible.
Use a check for the bottom so that when you hit the limit, the container goes back to an absolute position, but one set at the bottom: 0 of the parent.
Something like:
var limit = $('footer').offset().top;
var $mc = $('#mapContainer');
var pos = $mc.offset().top + $mc.outerHeight();
if (pos >= limit) {
$mc.removeClass('fixed')
.addClass('bottom-set').css('left',''); // define this in CSS for bottom absoluteness
}
#mapContainer.bottomFixed {
bottom: 0;
top: auto;
}
And to be fully honest, you might save yourself some time working this all out if you take a look at the ScrollToFixed plug-in (https://github.com/bigspotteddog/ScrollToFixed). I seem to be mentioning it quite a bit lately, but this issue seems to be a popular one right now.
Incidentally, go to your OP and click the Edit button. Shrink the height of your browser and scroll down. You should see SO has a fixed sidebar that passed the footer. ;)
I have created a fiddle to show a problem I'm having: http://jsfiddle.net/A5HyU/2/
On that fiddle, click on the button on the right, then go to the menu item Segnalibri, you'll notice that the submenu that opens has the second element in which the word Condiviso is somehow wrapped, but I would need the submenu to be sufficiently large to hold the whole word.
Is a problem of my setup or in the library itself?
To solve it I have tried two different solutions, the first is css based:
.jqSubMenu > li > a {
min-width: [XXX]px
}
This works, but requires me to pick a value which must be correct for every possible sub-menu I set up.
The second is js based:
$('.jqSubMenu').each(function() {
var l = 0;
$('> li > a', this).each(function() {
l = Math.max(l, $(this).text().trim().length);
}).css('min-width', (l * 8) + 'px');
});
This seems to work too, but slows down submenu initialization.
I also tried to change my surrounding css modifications, but none seem to affect the submenu layout.
You should try adding
white-space: nowrap;
To mentioned elements and see where does that get you.
I am sure this question may have been asked, but I couldn't find the right solution..
I need to show a menu on mouse over of an icon which can be placed anywhere on the page. Example code is here: ( http://jsfiddle.net/Qfjdk/2/ )
This shows the popup, however if I change the code from
#nav {
margin:0;
padding:0;
list-style:none;
float: left;
}
to
#nav {
margin:0;
padding:300px;
list-style:none;
float: right;
}
As can be seen, the position of the menu is always centered below. I want to change the position of the menu based on where the icon is displayed.
If the icon element is aligned right, I want to position the menu to start on right edge of icon. If the icon element is towards bottom left of displayed page, I want the menu bottom left to align with element top left etc..
I am not looking for the algorithm, am looking for the right way of setting parameters..
Appreciate your help
I would first define how many alternatives you want to support (for example, 4: button on right-top, right-bottom, left-top or left-bottom), and create a set of classes with these different options (.pos1, .pos2, .pos3, .pos4).
Then I'd use JS or jQuery to calculate the offset of the icon relative to the window. Depending on that result, I would give the popup the correct class from the ones you previously created. You can do this on load as well as on window resize. For example, calculating a distance to the right and applying css changes (you could apply a class instead, but this is just to show how it'd work):
var callback = function () {
// when hover on an element, calculate offset to right
$('.yourElement').mouseover(function(){
var currentElement = $(this);
var distanceRight = $(window).width() - ($(currentElement).offset().left + $(currentElement).width());
// If the distance is less than 50, then do...
if (distanceRight < 50) {
$('.yourPopup').css("left","-200px");
}
else {}
});
};
$(document).ready(callback);
$(window).resize(callback);
You can see the problem on http://jsfiddle.net/6gnAF/1/
I've little problem with my list elements.
I'm using very simple unordered list like;
<div>
<ul>
<li class='button'>Element 1</li>
...
<li class='button'>Element N</li>
</ul>
</div>
(Number of list elements, N, varies on every page.)
The div which contains the ul is 696 px width. List elements, li, are horizontally aligned, and when they reach end of div's width, they contiues on new line.
What I wanted to adding class .left the first li of every line, and class .right to the last one.
$('li').each(function() {
var liWidthOut = $(this).width();
totalWidth += liWidthOut;
if (totalWidth >= divWidth) {
$(this).addClass("left");
$(this).prev().removeClass("middle").addClass("right");
totalWidth = liWidthOut;
}
else {
$(this).addClass("middle");
}
});
I managed that with Jquery, but I've a little problem.
You can see the problem on http://jsfiddle.net/6gnAF/1/
The problem is, .left and .right classes change the width of li elements which are added to, so some times they create new lines, and last li elements become first one which have .right class, instead of .left.
How can I fix this?
(Sorry for the grammatically errors)
I changed the method to tracking the top position instead of calculating widths, seems to work ok. Using width() with paddings and borders and whatnot is less than reliable.
$('li')
// Add classes first, so we calculate on the right styles
.addClass("middle")
.addClass("button")
.each(function() {
// Compare with last position
if (lastPos != $(this).position().top) {
$(this).removeClass("middle").addClass("left")
.prev().removeClass("middle").addClass("right");
}
// Store last position
lastPos = $(this).position().top;
});
http://jsfiddle.net/2qydM/6/
Your code is mostly right, just two nitpicks which made it go wrong:
Use var liWidthOut = this.offsetWidth; instead of var liWidthOut =
$(this).width(); as the later is giving you the width of the inner
text, instead of also adding the padding, as offsetWidth does.
Apply the .button class before starting looping and calculating
widths, as the .button style will affect the elements width, you have to
apply it in the first place so your calculations don't fail later.
See working demo of your code with those two things corrected.
Would it be possible to separate these lists into seperate lists?
If so you could use :nth-child or :last-child
I'm working on a site which dynamically creates facebook-like buttons via PHP. However, the <div> that the buttons are contained in needs to have the CSS property overflow: hidden; This is the only way I've found that works to make a two-column format which forces both columns to expand to be the length of the longest one. The only problem with this method is that any facebook-like buttons placed near the bottom of the container get clipped when someone clicks on them and they expand.
Here's the way I've tried to solve this issue:
Using jQuery, I loop through all facebook like buttons on the page and calculate their document offset using the offset() method.
I then clone() each button as well as give it absolute positioning and the calculated offset using jQuery's css() method. I hope that each cloned button will be placed in the same position of the button it was cloned from when I append it to the document.
Finally, I change each old facebook-like button's css to visibility: hidden; in order to make it invisible but still take up the space it would have previously on the page. I add the clones of the facebook-like buttons to a div without the overflow: hidden; property using the appendTo() function.
Here's my entire code for this process:
// Replaces functional facebook recommend buttons with identical ones
// in a div where they won't get clipped when they expand.
// Hides the old buttons so space is still left for new ones
$(window).load(function(){
$(".fb-recommend").each(function(){ // cycle through each recommend button
var offset = $(this).offset(); // calculate offset of each button
var newButton = $(this).clone(); // clone the button
newButton.css({'position':'absolute', 'left':offset.left, 'top':offset.top});
$(this).css("visibility","hidden"); // hide the old button
newButton.appendTo("#wrapper"); // put the new button in the wrapper where it won't get clipped
});
});
At the end of all this, I expect to have clones of each button placed where the old button was but in a different <div>. The whole process works, except that the cloned facebook-like buttons are appearing at a slightly different position than the ones they were cloned from (as PitaJ points out they seem to be off by vertical offset multiples of around 39px). You can view the issue here:
LINK TO TEST WEBSITE
As you can see, the first button is placed in the correct location (the empty space filled by its hidden clone) but the other offsets were not calculated correctly.
I'd appreciate any ideas or help offered. Let me know if you'd like me to explain better or post more code!
Edit: I figured I'd post the CSS for the facebook-like buttons here (even though all I'm changing is the margin):
#content .fb-recommend {
margin: 15px 0 5px 0;
}
Edit 2: As per UnLoco's suggestion, I added a min-height property to the fb-reccommend CSS and commented out the line of code that was hiding the old buttons so it's easier to see the problem (which is still there, though slightly lessened. The CSS now looks like this:
#content .fb-recommend {
margin: 15px 0 5px 0;
min-height: 39px;
}
Edit 3: The problem appears to have been solved in all browsers but IE by changing the CSS to this:
.fb-recommend {
min-height: 24px; // I used 24 because the fb-buttons are 24px in height
}
Final Edit? This seems to work on all browsers on my end, including IE:
.fb-recommend {
min-height: 39px;
}
I'm thinking now that the 39 might have come from the 15px margin of the old fb-button + its 24px height. I think I can get around it by simply setting the height to be 39px and not having a margin.
this is because you are retrieving the offset before the fb iframes actually load. just add a css rule like this
div.fb-recommend{min-height:39px}
I believe you're problem is some odd jQuery weirdness.
To fix this, simple change your code to this:
$(window).load(function(){
$(".fb-recommend").each(function(index){ // cycle through each recommend button
var offset = $(this).offset(); // calculate offset of each button
var newButton = $(this).clone(); // clone the button
newButton.css({'position':'absolute', 'left':offset.left, 'top':offset.top + (39*index)});
$(this).css("visibility","hidden"); // hide the old button
newButton.appendTo("#wrapper"); // put the new button in the wrapper where it won't get clipped
});
});
This will account for the weird offset problem.