How to measure and set the width? - javascript

I would like to make sub-navigation to measure its parents width and then set its own width accordingly. At the moment every sub-navigation (.primary-navigation ul ul) gets an individual class (customWidth-0 + i). Then using this class I measure its parent's width and set the width minus the padding. It's all working nice and fine, but I'm learning and I'd like to shorten the script. I was trying to loop this, use "this", but seem to get stuck at every point. It would be nice to learn to do this in a proper, robust way. Any help is very much appreciated. Thanks.
jQuery(document).ready(function( ) {
jQuery(".primary-navigation ul ul").each(function(i) {
i = i+1;
jQuery(this).addClass("customWidth-0" + i);
});
a = jQuery(".customWidth-01").prev().parent().width();
b = jQuery(".customWidth-02").prev().parent().width();
c = jQuery(".customWidth-03").prev().parent().width();
d = jQuery(".customWidth-04").prev().parent().width();
jQuery(".customWidth-01").css("width", a-31);
jQuery(".customWidth-02").css("width", b-31);
jQuery(".customWidth-03").css("width", c-31);
jQuery(".customWidth-04").css("width", d-31);
});

I took your look and added my code after commenting your sections out and got the same thing.
<script type="text/javascript">
jQuery(document).ready(function( ) {
jQuery(".primary-navigation ul ul").each(function(i) {
i = i+1;
var _this = jQuery(this), w = _this.parents('ul').width();
_this.css("width", (w-31)+"px");
//jQuery(this).addClass("customWidth-0" + i);
console.log(i);
});
/*
a = jQuery(".customWidth-01").prev().parent().width();
b = jQuery(".customWidth-02").prev().parent().width();
c = jQuery(".customWidth-03").prev().parent().width();
d = jQuery(".customWidth-04").prev().parent().width();
jQuery(".customWidth-01").css("width", a-31);
jQuery(".customWidth-02").css("width", b-31);
jQuery(".customWidth-03").css("width", c-31);
jQuery(".customWidth-04").css("width", d-31);
*/
});
</script>
It will find the div above given ul and take its width take off 31px and then apply it to given elements below. The LI elements inside of the UL will already have a 100% width and conform to that standard. If you wanted to you could just apply a position: relative; on the LIs of the .primary-navigation and then position: absolute; left: 0; top: ~20px; (top is a little skewed according to your em size.. This will do the same thing except it won't wrap your children and make it look weird.
If you gave me an exact image of what you wanted your menu to look like (not using javascript or anything maybe a designed version??) I could probably do a better job as this is still kinda hard to answer. Hopefully the code I provided helps you if not leave another comment and let me know if you have questions.
Below line is old answer.
Set a variable
var _this = jQuery(this), w = _this.parent().parent().width();
_this.css("width", (w-31)+"px");// just to be safe added px use w/e or leave blank it assumes it
This should work for you the same as you have it in the foreach.
you could also use .parents('ul')
w = _this.parents('ul').width();

function() {
$('.primary-navigation').children('li').each(
function(index) {
var parentWidth = $(this).width();
$(this).children('ul').width(parentWidth/2);
}
);
Is this what you are looking for? now all the sub menu is 1/2 of parent. you need to fix it to your specific need.
DEMO http://jsfiddle.net/La07pvf2/

Related

Sticky block on JS

helloi want to make a sticky block using this script
$(window).scroll(function() {
var sb_m = 80; /* top and bottom padding */
var mb = 300; /* footer height with a margin */
var st = $(window).scrollTop();
var sb = $(".loginform");
var sbi = $(".loginform #loginform");
var sb_ot = sb.offset().top;
var sbi_ot = sbi.offset().top;
var sb_h = sb.height();
if(sb_h + $(document).scrollTop() + sb_m + mb < $(document).height()) {
if(st > sb_ot) {
var h = Math.round(st - sb_ot) + sb_m;
sb.css({"paddingTop" : h});
}
else {
sb.css({"paddingTop" : 0});
}
}
});
on naked HTML all work fine
if add a script to site (use wordpress) appears an infinite scroll
here can see problem
problem appears if add an element to footer through widgets
please tell me what is problem?
Honestly it's not ideal to work with padding-top, you should use top (with the element set as position:relative or absolute);
If you don't care about supporting IE, the easiest way is to use position:sticky; which does all the work for you: https://developer.mozilla.org/en-US/docs/Web/CSS/position
If you prefer to do it yourself, I'd suggest working this way:
store in variables the current offset().top and .left of the soon-to-be sticky element, outside any onScroll handlers.
When this condition is true:
$(window.scrollTop() >= theElementOffsetTopValueYouStored)
set the element as position: fixed and set the left: and top: properties of the element that should become sticky with the values you stored before. Then add your condition to make sure it stops when it reaches the end of the document, checking if element.offset().top + element.height > document.height
Please consider this is from memory, you might need to tweak a few things here and there to make it work properly.

Refresh Skrollr after resizing

I want the <nav> to become fixed after the user passes the first "block" (height: 100%;). I decided to use Skrollr because it is the only way I know to make the "change" immediately, without bugs on mobile and in IE. So I did this:
$("nav").attr("data-" + $("header").height(), "position: fixed;");
This is works great (the <nav> is right after the <header>), until you resizes the page. So I did this:
$(window).resize(function () {
var style = $("nav").attr("style");
$('nav').each(function() {
var attributes = this.attributes;
var i = attributes.length;
while( i-- ){
this.removeAttributeNode(attributes[i]);
}
})
$("nav").attr("data-" + $("header").height(), "position: fixed;");
$("nav").attr({"data-0": "position: absolute;", "style": style});
});
It takes the new height, and add it as a attr and deletes all others attr (because if not it will add you a lot: data-500, data-501, data-502, data-503...) and by looking at the code - it works great. The problem is that the Skrollr doesn't "sees" the change. what should I do?
Well, Thats was easy. One line instead of 20 lines.
var s = skrollr.init({
constants: {
menuresize: function() {
return $("header").height();
},
vh: '100p'
}
});
and to the nav I add data-_menuresize="position: fixed;" (and it explaines what menuresize
means in the code above).
Enjoy :D!

Is there any good way to determine the "actual content width" of a web page?

Here's what StackOverflow looks like on my (huge) work monitor:
That is a lot of white space on either side of the site's actual content.
I get that this is how a very large percentage of websites are designed—so I'm not singling out SO here—but that's actually exactly why I'm asking this question. I'm thinking it'd be really nice if I had some reliable way (say, via JavaScript) of determining the "actual" width of a website, which I could then use to write a quick script that would auto expand any site I'm browsing to fill the available width on my monitor. As it is, I find it absurd that sometimes I still squint before reading tiny text before realizing/remembering to zoom in to take advantage of my enormous screen.
Ahh... much better.
I suspect this is possible, at least to a reasonable degree via some heuristic, as my Android phone appears to do something a lot like this when I double-tap on the screen while browsing the web.
This will do something sorta like that. Though probably misses all kinds of edge cases.
// Assuming jQuery for simplicity
var drillIn = function(node) {
var max = 0;
var windowWidth = $(window).width();
var result = 0;
$(node).children().each(function() {
var $this = $(this);
if ($this.width() > max) {
max = $this.width();
}
});
if (0 < max && max < windowWidth) {
return max;
} else {
$(node).children().each(function() {
var childMax = drillIn(this);
if (childMax > result) {
result = childMax;
}
});
return result;
}
};
drillIn(document.body);
Working Fiddle: http://jsfiddle.net/bdL5b/1/
On SO, I get 960 which is right. Basically it drills into the DOM tree to find the widest node closest to the root which is not 0 or the window width. Because usually, close to the root node there is a container node which holds the site content. Usually.
Not sure you will get a 100% reliable solution though. This is a tricky thing because there are a TON of ways to style websites. I bet crazy stuff like horrible use of absolute positioning could be a serious thorn in your ass.
If you use Firefox, Greasemonkey is awesome. It will run Javascript that you write on any page (I have used it on Stack Overflow's site before).
Just use the browser's built-in "inspect element," to get the id of whatever you want to expand and do this:
document.getElementById("content").style.width = "100%"; // content is just an example
I think the class name of the middle boxes is .container so you could do this:
var boxes = document.getElementsByClassName("container");
for(var i = 0; i < boxes.length; i++)
{
boxes[i].style.width = "100%";
}
As far as a heuristic for doing this arbitrarily, there's probably no good way to do it to all web pages in an unbiased way, without significantly messing up the site's appearance.
That being said, this or something similar might work ok:
var divs = document.getElementsByTagName("div");
for(var i = 0; i < divs.length; i++)
{
divs[i].style.minWidth = "90%";
}
Ha! I've got something close (though I'm also going to try Alex's approach):
The following relies on jQuery and is arguably inefficient (it inspects, I believe, every element in the DOM); but it doesn't take any time on my machine and at least works with SO:
(function($) {
function text($element) {
return $.trim($element.clone().children().remove().end().text());
}
function hasContent($element) {
return $element.is(":visible") && text($element).length > 0;
}
function getExtremeEdges($elements) {
var extremeLeft = null;
var extremeRight = null;
$.each($elements, function(i, el) {
var $element = $(el);
var offset = $element.offset();
if (!extremeLeft || offset.left < extremeLeft) {
extremeLeft = offset.left;
}
if (!extremeRight || (offset.left + $element.width()) > extremeRight) {
extremeRight = offset.left + $element.width();
}
});
return [extremeLeft, extremeRight];
}
var $elementsWithContent = $("*").filter(function(i, el) {
return hasContent($(el));
});
var extremeEdges = getExtremeEdges($elementsWithContent);
var width = extremeEdges[1] - extremeEdges[0];
var desiredWidth = $(document).width() * 0.95;
if (width < desiredWidth) {
$("body").css("zoom", (desiredWidth / width));
}
}(jQuery));
Minified (to use as a bookmarklet):
(function(a){function b(b){return a.trim(b.clone().children().remove().end().text())}function c(a){return a.is(":visible")&&b(a).length>0}function d(b){var c=null;var d=null;a.each(b,function(b,e){var f=a(e);var g=f.offset();if(!c||g.left<c){c=g.left}if(!d||g.left+f.width()>d){d=g.left+f.width()}});return[c,d]}var e=a("*").filter(function(b,d){return c(a(d))});var f=d(e);var g=f[1]-f[0];var h=a(document).width()*.95;if(g<h){a("body").css("zoom",h/g)}})(jQuery);
Time to dogfood this puppy for a while...
I think each website will be too different to have a standard was of auto resizing their content. I belive CSS is the key, by using user defined style sheets. Or something like Stylish. See https://superuser.com/questions/128666/custom-per-site-stylesheet-extension-for-firefox
or https://addons.mozilla.org/en-us/firefox/addon/style-sheet-chooser-ii/
Not much progress but I'm putting what I tried up in case it inspires anyone else:
Works much worse than you would think
Make a bookmarklet that makes all children of body have 100% width. Then, if you click the bookmarklet again, it makes all children of children of body have 100% width. This way, the user can just click until the site becomes more pleasing to them :)
var levels = levels ? levels + 1 : 1;
$('body *:nth-child(' + levels + ')').css({ width: '100%' });
.
First approach to try and figure out where the first meaningful content is
Cool puzzle, I'm employing the awesomeness of jQuery. So I'm approaching it by trying to find the first element which has more non-empty .contents() than .children() because contents also fetches text nodes. Here's what I have so far. It's close, but not quite right because it seems to be searching a bit too deep:
$('body *:visible').filter(function(){
return moreNonEmptyContentThanChildren($(this));
}).first();
function moreNonEmptyContentThanChildren(el) {
var contentCount = 0;
var contents = el.contents();
for (c = 0; c < contents.length; c++) {
elc = contents[c];
if (elc.nodeType != 3 || (elc.nodeType == 3 && $.trim($(elc).text()) != '')) {
contentCount ++;
}
}
return contentCount != el.children().length;
}

Getting Coordinates of an element on page scroll

I am having this problem where i have a set of 6 UL's having a common class x.Each of them consist of a specific section of the page.Now i have 6 menus that are related to each of the section.What i have to do is highlight the menu when its related section is in users view.
For this i thought that may be jQuery position(); or offset(); could have helped but they give the top and left of the element.I also tried using jQuery viewport plugin but apparently view port is big it can show more than one UL at a time hence i cant apply element specific logic here.I am not familliar to this but does anything changes of an element on scrolling?If yes then how to access it?
Please share your views.
Regards
Himanshu Sharma.
Is very easy to do it using jQuery and a dummy fixed HTML block that helps you find the current position of the viewport.
$(window).on("scroll load",function(){
var once = true;
$(".title").each(function(ele, index){
if($(this).offset().top > $("#viewport_helper").offset().top && once){
var index = $(this).index(".title");
$(".current").removeClass('current')
$("#menu li").eq(index).addClass('current')
once = false;
}
});
})
Check out a working example: http://jsfiddle.net/6c8Az/1/
You could also do something similar with the jQuery plugin, together with the :first selector:
$(window).on("scroll load",function(){
$(".title:in-viewport:first").each(function(){
var index = $(this).index(".title");
$(".current").removeClass('current')
$("#menu li").eq(index).addClass('current')
});
})
You can get the viewport's width and height via $(document).width() and $(document).height()
You can get how many pixels user scrolls via $(document).scrollTop() and $(document).scrollLeft
Combining 1 and 2, you can calculate where the viewport rectangle is
You can get the rectangle of an element using $(element).offset(), $(element).width() and $(element).height()
So the only thing left to you is to determine whether the viewport's rectangle contains (or interacts) the elements's rectangle
So the whole code may look like:
/**
* Check wether outer contains inner
* You can change this logic to matches what you need
*/
function rectContains(outer, inner) {
return outer.top <= inner.top &&
outer.bottom >= inner.bottom &&
outer.left <= inner.left &&
outer.right >= inner.right;
}
/**
* Use this function to find the menu related to <ul> element
*/
function findRelatedMenu(element) {
return $('#menu-' + element.attr('id'));
}
function whenScroll() {
var doc = $(document);
var elem = $(element);
var viewportRect = {
top: doc.scrollTop(),
left: doc.scrollLeft(),
width: doc.width(),
height: doc.height()
};
viewportRect.bottom = viewportRect.top + viewportRect.height;
viewportRect.right = viewportRect.left + viewportRect.width;
var elements = $('ul.your-class');
for (var i = 0; i < elements.length; i++) {
var elem = $(elements[i]);
var elementRect = {
top: elem.offset().top,
left: elem.offset().left,
width: elem.width(),
height: elem.height()
};
elementRect.bottom = elementRect.top + elementRect.height;
elementRect.right = elementRect.left + elementRect.width;
if (rectContains(viewportRect, elementRect)) {
findRelatedMenu(elem).addClass('highlight');
}
}
}
$(window).on('scroll', whenScroll);
Let's see if i understood well. You have a page long enough to scroll, and there is an element that when it appears in the viewport, you wanna do something with it. So the only event that's is triggered for sure on the time the element gets in the viewport is the 'scroll'. So if the element is on the page and the scroll is on the viewport, what you need to do is bind an action to the scroll event to check if the element is in the view each time the event is trigger. Pretty much like this:
$(window).scroll(function() {
check_element_position();
});
Now, in order for you to know if the element is in the viewport, you need 3 things. The offset top of that element, the size of the viewport and the scroll top of the window. Should pretty much look like this:
function check_element_position() {
var win = $(window);
var window_height = win.height();
var element = $(your_element);
var elem_offset_top = element.offset().top;
var elem_height = element.height();
var win_scroll = win.scrollTop();
var pseudo_offset = (elem_offset_top - win_scroll);
if (pseudo_offset < window_height && pseudo_offset >= 0) {
// element in view
}
else {
// elem not in view
}
}
Here, (elem_offset_top - win_scroll) represent the element position if there was no scroll. Like this, you just have to check if the element offset top is higher then the window viewport to see if it's in view or not.
Finally, you could be more precise on you calculations by adding the element height (variable already in there) because the code i just did will fire the event even if the element is visible by only 1 pixels.
Note: I just did that in five minutes so you might have to fix some of this, but this gives you a pretty darn good idea of what's going on ;)
Feel free to comment and ask questions

Find DOM elements at top and bottom of scrolling div with jQuery

I have a scrolling div containing list items. I have this boilerplate scroll event defined
$("#scrollingDiv").scroll(function(e) {
});
Inside of this scroll event function, how can I figure out which elements are at the top and bottom of the currently visible area?
You could try computing the positions of the list items with respect to the scrolling <div> and then scan the positions to see which ones match up with the scrollTop of the <div>.
Something like this perhaps:
var base = $('#scrollingDiv').offset().top;
var offs = [ ];
$('li').each(function() {
var $this = $(this);
offs.push({
offset: $this.offset().top - base,
height: $this.height()
});
});
$("#scrollingDiv").scroll(function() {
var y = this.scrollTop;
for(var i = 0; i < offs.length; ++i) {
if(y < offs[i].offset
|| y > offs[i].offset + offs[i].height)
continue;
// Entry i is at the top so do things to it.
return;
}
});
Live version (open your console please): http://jsfiddle.net/ambiguous/yHH7C/
You'd probably want to play with the fuzziness of the if to get something that works sensibly (1px visible hardly makes an element the top one) but the basic idea should be clear enough. Mixing in the height of #scrollingDiv will let you see which <li> is at the bottom.
If you have a lot of list items, then a linear search might not be what you want but you should be able to solve that without too much effort.

Categories

Resources