I am working om a menu bar, each menu bar item is an image, when user places mouse over menu item a div with submenu will appear.
I want to place div directly under the appropriate image item (no space, and div will hover above all elements), with right side alignment, meaning the right top corner of div should be under bottom right corner of image.
Because I can't and don't want to hard code position of divs, i want to do it dynamically.
For now I have this:
$('img').each(function(){
jQuery(this).mouseenter(function(){
var menuItem = $('#' + this.id + '_menu'); //get the needed div
var imgRight = this.offset() + this.width();
});
});
The offset() method has top and left properties, you need use them, example:
var imgRight = this.offset().left + this.width();
var imgTop = this.offset().top + this.height();
After that, you will have to give the absolute positioning to the DIVs to place them below the images:
menuItem.css({
position:'absolute',
top: imgTop,
left: imgLeft,
zIndex:5000
});
So your code becomes:
$('img').each(function(){
jQuery(this).mouseenter(function(){
var menuItem = $('#' + this.id + '_menu'); //get the needed div
var imgRight = this.offset().left + this.width();
var imgTop = this.offset().top + this.height();
menuItem.css({
position:'absolute',
top: imgTop,
left: imgLeft,
zIndex:5000
});
// now show the corresponding div
menuItem.show('slow');
});
});
More Info:
http://api.jquery.com/offset/
You shouldn't have to hard code or calculate the position of these items. Any of the following CSS rules should achieve your goal: position: relative; right: 0 or float: right:.
It'd be good to see some of your markup for additional testing. www.jsfiddle.net is a great resource for this.
There are 2 ways to do this: the correct-way or the cheat way...
The correct way: you need to get the top and client height of the actuating object - client heights no prob just call it - but the top means you must get the to of all the parent objects too - use this:
function J_pos(o)
{
var x,y;
y=o.offsetTop;
x=o.offsetLeft;
o=o.offsetParent;
while(o)
{
y+=o.offsetTop;
x+=o.offsetLeft;
o=o.offsetParent;
}
return [x,y];
};
Now the top and client height you do this:
<div style=top:"+(p[0]+obj.clientHeight)+";left:"+p[1]>
The cheat-way (not so dynamic - but quick):
put a tag like a <span> around the actuating (mouseover) object. Make it position-relative. Place a <div> inside it:
<div id="ABC" style="position:absolute;left:0;display:none">
Now on mouseover put document.getElementById("ABC").style.display="" and bottom:0 — boom baby dusted. Downside to this is you have to manually do it for each instance, but if you only have 3 or so well bingo.
Related
In the fiddle you will see at the center of the page a DIV that contains text next to an img.
When I scroll down/up I need to effect with jquery/javascript only the div who's the closest to the navbar-below. all the divs as the same class so I effect them all-not what I need
For example:
what I am trying to achieve : when I scroll down,the closest div to the navbar(yellow bar) will be painted(the div) green,so if I scroll down and the navbar "collapse" with the div with will paint in green, and when he passes him and "disapper" it will go back to original color and the next div will paint in green. is it possible?
Here's the JS FIDDLE
When I referred to div I meant this section :
<div class="x" id="inside_center">
<div class="left_side" id="left_inside_center">sddsadasasdsadLorem </div>
<div class="right_side" id="right_inside_center"><img src="http://img-9gag-lol.9cache.com/photo/a7KwPAr_460s.jpg"></div>
</div>
EDIT:
UPDATED JSFIDDLE :
http://jsfiddle.net/nnkekjsy/3/
I added my jquery,as you can see it works only for the first one,and then stuck.. i need to "pass" it along the others div below him when the are getting to the same point. any ideas? :
$(document).ready(function() {
$(window).scroll(function() {
var scrollVal = $(this).scrollTop();
var navHeight = $("#div_menu").outerHeight();
if ( scrollVal > 55) {
$('#left_inside_center').css({'position':'fixed','top' :navHeight+'px'});
} else {
$('#left_inside_center').css({'position':'static','top':'auto'});
}
});
});
Have you tried use the first-of-type to select the top div, if i understand what your trying to do.
CSS3 selector :first-of-type with class name?
An other solution would be to check the position of the div and the nav bar and pick the closest one.
$(".left_side").each(function () {
//compare scroll with div
if(window.scrollTop() = $(this).position.top())
{
//do something
}
});
I know the position and the scroll won't be the same value but you can play with the condition to put some range.
Edit :
I think this is what you want. The navHeight and the height variable should be outside the window.scroll function as they never change :
$(window).scroll(function() {
var scrollVal = $(this).scrollTop();
var navHeight = $("#div_menu").outerHeight();
var height = parseInt($(".right_side").css("height").split("px")[0]);
$(".left_side").css({'position':'static','top':'auto'});
$(".left_side").filter(function( ) {
return $(this).position().top - 10 < scrollVal && $(this).position().top + height > scrollVal;
}).css({'position':'fixed','top' :navHeight+'px'});
});
Working fiddle :
http://jsfiddle.net/nnkekjsy/6/
I have set up an example where i want to calculate position of nested element to it parent element.
For example http://jsfiddle.net/45cJZ/ in this example on click event i need to calculate position of nested div element to it parent element i.e .wrapper class.
can this be done using .offsetParent()
Add position:relative to the .wrapper (to make it the parent for the position calculations) and use .position() to get the position of the clicked element relative to the .wrapper
HTML
<div class="wrapper">
<div class="div1"></div>
<!-- ... -->
</div>
CSS
.wrapper {
/*...*/
position: relative;
}
Javascript
$(".wrapper").children().on("click", function() {
console.log($(this).position());
});
Demo: http://jsfiddle.net/8d62J/
#Andreas' answer is the best and correct but since you mentioned offset you can see how that works like this.
$('.wrapper div').on('click', function (event) {
var myOffset = $(this).offset();
var myParentOffset = $(this).parent().offset();
console.log(myOffset, myParentOffset);
var topDiff = myOffset.top - myParentOffset.top;
var leftDiff = myOffset.left - myParentOffset.left;
console.log(topDiff, leftDiff);
});
Demo: http://jsfiddle.net/robschmuecker/45cJZ/1/
You can use jQuery .position() to get the coordinates of element relative to its parent. Here is your fiddle edited to show the functionality of .position(). Just change the class to see the alerts change. All you have to do is:
var p = $(".div2").position();
alert("Left: " + p.left);
alert("Top: " + p.top);
CSS
.page{
width: 275px;
hight: 380px;
overflow: auto;
}
HTML
<div class="page">dynamic text</div>
How to create a new div when the dynamic text is overflowing past the fixed height of the div?
Example:
<div class="page">Some dynamic texts will appear here</div>
When the dynamic text is overflowing past the fixed height of the div, the content above will be appear like this.
<div class="page">Some dynamic</div>
<div class="page">texts will</div>
<div class="page">appear here</div>
I've tried using wordwrap function in PHP wordwrap($dynamic_text, 600, '</div><div class="page">'); it's can running, but it had a problem when the character was copied from Ms.Words.
So, by detecting the overflowing text, cut it, and then paste it into the new div element is the better solustion, i guess. But, I don't know how to do this solution using JQuery or Javascript.
Any ideas? Thanks in advance!
You can do it, and it's way more than just a couple lines of code. It took a very experienced developer a couple days. Sorry, can't share the code.
Javascript: Put the whole content into the div. You may keep it hidden or out of the DOM for a while. Traverse the div's children. Find the one whose top+scrollHeight exceeds the div's height. Traverse it recursively. Eventually, you will either find an indivisible element, e.g., an image, that doesn't fit, or a position within a text node to split the text at. Remove that part and all further elements from the div. Add them to a new one.
There are a lot of details to address, so it's not simple. But doable.
I just had something similar working today while I was searching for an answer but there doesn't seem to be anything straight forward.
Although I am using Array.reduce() you should be able to do this with Array.forEach() or any other iterating code you like.
words.reduce(function(acc, value)
This is done by calculating if the element will overflow if we add another word to it before we actually render it. The hacky thing here is to add another block element inside of it with visibility: hidden.
element.innerHTML = '<div style="visibility: hidden; height: 100%; width=100%">' + textToBeAdded + '</div>';
That way the block element still takes its parents dimensions and the parent element can be checked for overflow.
The way to check for overflow is to compare the element's scrolling height to its height:
if (element.scrollHeight > element.offsetHeight)
If it overflows we leave it as is and create a new element and put the current value (word) in the iteration. Then we attach it to the same DOM tree as the previous element (as its parent's child... like having a new brother 😜)
var newPageEl = document.createElement('div');
newPageEl.classList = 'page';
newPageEl.textContent = word;
parentElement.appendChild(newPageEl);
Hope this makes sense.
var page = document.getElementsByClassName('page')[0];
if (page.scrollHeight > page.offsetHeight) {
// is overflowing
fixOverflow(page);
}
function fixOverflow(element) {
var words = element.textContent.split(' ');
// delete previous text content
element.textContent = '';
words.reduce(function(acc, value) {
// store current element's text
var currentElementText = element.textContent.toString().trim();
var textToBeAdded = currentElementText + ' ' + value;
element.innerHTML = '<div style="visibility: hidden; height: 100%; width=100%">' + textToBeAdded + '</div>';
if (element.scrollHeight > element.offsetHeight) {
// is overflowing with the new word
element.innerHTML = "";
// leave the last page element as is
element.textContent = currentElementText;
// create another element with the new value to be added
// ** IMPORTANT replace the memory of the previous element variable
element = createPageElement(value);
// add it to the same DOM tree as the previous page element
page.parentElement.appendChild(element); // could also be document.getElementById('page-container').appendChild(element);
} else {
// if not overflowing add another word
element.innerHTML = currentElementText + ' ' + value;
}
}, "");
}
function createPageElement(text) {
// create element with class page
var newPageEl = document.createElement('div');
newPageEl.classList = 'page';
newPageEl.textContent = text;
return newPageEl;
}
Pleasantries
I've been playing around with this idea for a couple of days but can't seem to get a good grasp of it. I feel I'm almost there, but could use some help. I'm probably going to slap myself right in the head when I get an answer.
Actual Problem
I have a series of <articles> in my <section>, they are generated with php (and TWIG). The <article> tags have an image and a paragraph within them. On the page, only the image is visible. Once the user clicks on the image, the article expands horizontally and the paragraph is revealed. The article also animates left, thus taking up the entire width of the section and leaving all other articles hidden behind it.
I have accomplished this portion of the effect without problem. The real issue is getting the article back to where it originally was. Within the article is a "Close" <button>. Once the button is clicked, the effect needs to be reversed (ie. The article returns to original size, only showing the image, and returns to its original position.)
Current Theory
I think I need to retrieve the offset().left information from each article per section, and make sure it's associated with its respective article, so that the article knows where to go once the "Close" button is clicked. I'm of course open to different interpretations.
I've been trying to use the $.each, each(), $.map, map() and toArray() functions to know avail.
Actual Code
/*CSS*/
section > article.window {
width:170px;
height:200px;
padding:0;
margin:4px 0 0 4px;
position:relative;
float:left;
overflow:hidden;
}
section > article.window:nth-child(1) {margin-left:0;}
<!--HTML-->
<article class="window">
<img alt="Title-1" />
<p><!-- I'm a paragraph filled with text --></p>
<button class="sClose">Close</button>
</article>
<article class="window">
<!-- Ditto + 2 more -->
</article>
Failed Attempt Example
function winSlide() {
var aO = $(this).parent().offset()
var aOL = aO.left
var dO = $(this).offset()
var dOL = dO.left
var dOT = dO.top
var adTravel = dOL-aOL
$(this).addClass('windowOP');
$(this).children('div').animate({left:-(adTravel-3)+'px', width:'740px'},250)
$(this).children('div').append('<button class="sClose">Close</button>');
$(this).unbind('click', winSlide);
}
$('.window').on('click', winSlide)
$('.window').on('click', 'button.sClose', function() {
var wW = $(this).parents('.window').width()
var aO = $(this).parents('section').offset()
var aOL = aO.left
var pOL = $(this).parents('.window').offset().left
var apTravel = pOL - aOL
$(this).parent('div').animate({left:'+='+apTravel+'px'},250).delay(250, function() {$(this).animate({width:wW+'px'},250); $('.window').removeClass('windowOP');})
$('.window').bind('click', winSlide)
})
Before you go scratching your head, I have to make a note that this attempt involved an extra div within the article. The idea was to have the article's overflow set to visible (.addclass('windowOP')) with the div moving around freely. This method actually did work... almost. The animation would fail after it fired off a second time. Also for some reason when closing the first article, the left margin was property was ignored.
ie.
First time a window is clicked: Performs open animation flawlessly
First time window's close button is clicked: Performs close animation flawlessly, returns original position
Second time SAME window is clicked: Animation fails, but opens to correct size
Second time window's close button is clicked (if visible): Nothing happens
Thank you for your patience. If you need anymore information, just ask.
EDIT
Added a jsfiddle after tinkering with Flambino's code.
http://jsfiddle.net/6RV88/66/
The articles that are not clicked need to remain where they are. Having problems achieving that now.
If you want to go for storing the offsets, you can use jQuery's .data method to store data "on" the elements and retrieve it later:
// Store offset before any animations
// (using .each here, but it could also be done in a click handler,
// before starting the animation)
$(".window").each(function () {
$(this).data("closedOffset", $(this).position());
});
// Retrieve the offsets later
$('.window').on('click', 'button.sClose', function() {
var originalOffset = $(this).data("originalOffset");
// ...
});
Here's a (very) simple jsfiddle example
Update: And here's a more fleshed-out one
Big thanks to Flambino
I was able to create the effect desired. You can see it here: http://jsfiddle.net/gck2Y/ or you can look below to see the code and some explanations.
Rather than having each article's offset be remembered, I used margins on the clicked article's siblings. It's not exactly pretty, but it works exceptionally well.
<!-- HTML -->
<section>
<article>Click!</article>
<article>Me Too</article>
<article>Me Three</article>
<article>I Aswell</article>
</section>
/* CSS */
section {
position: relative;
width: 404px;
border: 1px solid #000;
height: 100px;
overflow:hidden
}
article {
height:100px;
width:100px;
position: relative;
float:left;
background: green;
border-right:1px solid orange;
}
.expanded {z-index:2;}
//Javascript
var element = $("article");
element.on("click", function () {
if( !$(this).hasClass("expanded") ) {
$(this).addClass("expanded");
$(this).data("originalOffset", $(this).offset().left);
element.data("originalSize", {
width: element.width(),
height: element.height()
});
var aOffset = $(this).data("originalOffset");
var aOuterWidth = $(this).outerWidth();
if(!$(this).is('article:first-child')){
$(this).prev().css('margin-right',aOuterWidth)
} else {
$(this).next().css('margin-left',aOuterWidth)
}
$(this).css({'position':'absolute','left':aOffset});
$(this).animate({
left: 0,
width: "100%"
}, 500);
} else {
var offset = $(this).data("originalOffset");
var size = $(this).data("originalSize");
$(this).animate({
left: offset + "px",
width: size.width + "px"
}, 500, function () {
$(this).removeClass("expanded");
$(this).prev().css('margin-right','0')
$(this).next().css('margin-left','0')
element.css({'position':'relative','left':0});
});
}
});
Is there any way to dynamically draw an arrow between the two highlighted list items?
So if I hovered over "Item 2" it would do this (but a straight arrow):
Item 1 Highlight 3
Item 2-----\ Highlight 1
Item 3 ----->Highlight 2
This is the code from the answer I got here a few mins ago:
Highlight item in two lists when mouseover
$(".list1 li, .list2 li").hover(function () {
var n = this.id.substr(2);
$("#qq" + n + ", #aa" + n).toggleClass("highlight");
});
jsfiddle: http://jsfiddle.net/e37Yg/1/
<ul class="list1">
<li id="qq1">sdfsdv</li>
<li id="qq2">bnvnvb</li>
<li id="qq3">nmnutymnj7</li>
<li id="qq4">cvbc</li>
<li id="qq5">45tsgd</li>
</ul>
<ul class="list2">
<li id="aa3">fgtbrtgb</li>
<li id="aa1">vbn xgbn</li>
<li id="aa5">vdgver</li>
<li id="aa4">asdasdv</li>
<li id="aa2">nvfbnfn</li>
</ul>
You don't have to use 2D drawing here. Check this out: http://jsfiddle.net/vjYuW/
I just forked and updated the fiddle you have posted above.
Here is the essential code, it handles 3 DIVs 1 pixel wide or tall to draw the lines:
HTML:
...left list...
<div id="segment1" class="hline"></div>
<div id="segment2" class="vline"></div>
<div id="segment3" class="hline"></div>
...right list...
CSS:
... formatting for ULs here, both have to be float:left...
.highlight { background-color: red; }
.hline {
display:block;
position:relative;
float:left;
height: 1px;
width: 7em;
}
.vline {
display:block;
position:relative;
float:left;
height: 1px;
width: 1px;
}
JavaScript:
$(".list1 li, .list2 li").hover(function () {
var n = this.id.substr(2);
var leftY = $('#qq' + n).position().top;
var rightY = $('#aa' + n).position().top;
var H = Math.abs(rightY-leftY);
if (H == 0) H = 1;
$('#segment1').css('top',leftY+'px');
$('#segment3').css('top',rightY+'px');
$('#segment2').css('top',Math.min(leftY,rightY)+'px');
$('#segment2').css('height',H+'px');
$("#qq" + n + ", #aa" + n + ",#segment1,#segment2,#segment3").toggleClass("highlight");
});
Note: you will probably have to tweak it a little to support all browsers - I didn't check IE6 & Co.
You can use the HTML5 canvas element to achieve this.
I'm not sure if this is the best way to do it, but I fiddled around and got this.
What I did is first I enclosed the lists in a div. The div is styled with CSS to have a relative position. This is so when you get the position with jQuery, it will give a position relative to that. Next, I put a canvas in front of the lists and disabled pointer events on it. I also added something to adjust the height of the canvas to the height of the lists. Then I added another handler for hover. When you hover over it, it will draw the arrow, and when you hover out, it'll clear the canvas.
To draw the arrow is fairly simple. First it gets the positions of the items. Then it draws a line and uses some math to orient the arrow. To get the positions is fairly easy. For the right list, you can just use the position method. For the left list, I created a temporary span and appended it to the list item, and then got the position of that.
I think for something like this you may want to use a third party drawing library such as Vector Draw Library.
You can download the library from the link and add it to your app. Then:
Include it on your page:
<script type="text/javascript" src="wz_jsgraphics.js"></script>
Then add to your hover function:
$(".list1 li, .list2 li").hover(function () {
var n = this.id.substr(2);
$("#qq" + n + ", #aa" + n).toggleClass("highlight");
//canvas is your drawing div
var jg = new jsGraphics("canvas");
jg.drawLine($("#qq" + n).offset().left + 30, $("#qq" + n).offset().top , $("#aa" + n).offset().left, $("#aa" + n).offset().top );
jg.paint();
Note that you will have to write the code to remove the line in the hover function otherwise once it is drawn it will remain. Also, I am using offset() to calculate the position of the items in the list. This should work but you may have to tweak a bit to get it to look right.
The above code works but is not complete. Maybe the second function in the hover can call clear() on the canvas. Canvas here is the enclosing div that encloses the two lists.
<script src='www.walterzorn.de/en/scripts/wz_jsgraphics.js'> </script>
function drawLine(element1, element2) {
var jg = new jsGraphics("renderGraph");
var ele1 = document.getElementById(element1);
var ele2 = document.getElementById(element2);
jg.setColor("#DDD");
jg.setStroke(5);
jg.drawLine(ele1.offsetLeft + ele1.offsetWidth/2 , ele1.offsetTop + ele1.offsetHeight/2, ele2.offsetLeft + ele2.offsetWidth/2, ele2.offsetTop + ele2.offsetHeight/2);
jg.paint();
}