I'm figuring out a possible solution for a dynamic crossfade of HTML elements. The core of my problem is a strange behaviour of the jQuery's .position() and updating the css "position" property after retrieving the old position.
I've made a JSFiddle to illustrate my problem: http://jsfiddle.net/svenhanssen/DDYVs/
/*
This works. I'll get a position.top from 0 to 90
*/
$("p").each(function( p ) {
var position = $(this).position();
console.log(position.top);
});
/*
This doesn't work. I'll get a position.top of 0 for all elements. Why does the css set effects the position?
*/
$("p").each(function( p ) {
var position = $(this).position();
console.log(position.top);
$(this).css({
position: "absolute"
});
});
Somehow changing the css "position" property afterwards effects the old property. Does anyone know the reason why and a possible solution?
The moment you set a <p> to position: absolute it is taken out of the document flow, and the next non-absolute <p> is moved upwards to take the freed space. Then you get to that just-repositioned <p> element, and sure enough its top is now 0 (since there are no in-flow elements before it to push it down).
Here's a possible solution:
$("p").each(function( p ) {
var position = $(this).position();
console.log(position.top);
}).css({
position: "absolute"
});
Note that now all <p> elements are set to position: absolute only after the loop has rolled.
Updated fiddle
Related
I created a jsbin which has a div with labels
And I want to scroll to a specific label.
however it is working correctly only if margin-top is 0.
HOwever If I test it with margin-top:100px it is not accurate... http://jsbin.com/idinob/8/edit
why is that ?
The animate command is :
$('.d').animate({
scrollTop: $(".s210").position().top
}, 200);
});
And I did use position and not offset because im talking a about a span which is inside the div. so we are talking about position and not offset.
it seems that $(".s210").position().top is affected by the margin-top.
why ?
You should set the position property to relative:
span { position: relative }
I try to use a dojo moveable with a fixed position in the browser window.
Unfortunatly everytime I move the div with the mouse the position is set to absolute. What can I do to make the div fixed ?
html:
<html>
<body>
<div id="moveMe" style="position:fixed;width:100px;height:100px;border:1px solid black;background-color:#00ff00;cursor:pointer;">bla</div>
<p>test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>
test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>
test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>
test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>
test<br>test<br>test<br>test<br>test<br>test<br>test<br>test<br>
</body>
</html>
script:
dojo.require("dojo.dnd.move");
dojo.ready(function(){
var pcm = new dojo.dnd.move.boxConstrainedMoveable(dojo.byId("moveMe"), {
box : dojo.window.getBox(),
within : true
});
});
Link to test:
http://jsfiddle.net/zPVdX/
cheers,
krater
position: fixed and position: absolute are two completely opposing methods used by the browser to determine how an element is positioned.
I'd first recommend reading up on the differences between them: http://css-tricks.com/absolute-relative-fixed-positioining-how-do-they-differ/
Hopefully now you will understand why Dojo draggable needs to set your element to an absolute position. This allows Dojo to visually move the draggable element via it's top: and left: properties.
Compare this with static positioning, which will attempt to anchor your element to a position relative to the current viewport.
If you add more detail on what are you visually trying to accomplish, there may be another solution.
You could use event 'MoveStop' on your DND (draggable element) in order to forcefully add position: fixed; at the END of your drag action. In this way you would be able to drag around your element using position: absolute; and have it have fix position when the drag is finished.
Some pesudo code
yourDnd.on('MoveStop', function (e) {
// Set position FIXED
domStyle.set(this.node, {
'position': 'fixed ',
});
});
More info on events can be found here:
http://livedocs.dojotoolkit.org/dojo/dnd
Regarding this hack I would not suggest you to change DOJO library as your change could be not safe and create bugs in other part of the framework.
I just got this working with:
dnd.on('MoveStart', function (e) {
var p = dojo.position(e.node, true);
var parent_position = dojo.position(e.node.parentNode, true)
dojo.style(e.node, "top", p.y - parent_position.y + "px");
dojo.style(e.node, "position", "absolute");
});
dnd.on('MoveStop', function (e) {
var p = dojo.position(e.node, false);
dojo.style(e.node, "top", p.y + "px");
dojo.style(e.node, "position", "fixed");
});
I'm writing a simple card game in JavaScript, and I'd like to have a "card" img snap to its target when played. I'm accomplishing this by removing the img from its current parent and placing it in a new parent (the target). However, when I place the img in its new container with jQuery's .appendTo(), the card snaps to the corner of the screen.
See this demo for a working example.
Is there a reason the div snaps to the corner of the screen when it's moved to a new parent? I'm wondering if there are css properties that are being inherited/lost behind the scenes when I change the div's parent.
Any help would be appreciated.
- EDIT -
Additional information: while it has been suggested that I simply add this to my drop handler (which works) -
$(ui.draggable).css({'top' : '0', 'left' : '0'});
I'd like to .animate() to this position, so the snap isn't instantaneous. While the card does end up in the correct spot, it first snaps to the corner and then eases into the correct position. I'd like to avoid that first snap to the corner.
$(ui.draggable).css({'top' : '0', 'left' : '0'});
Sorry, I had to leave for work, but try this:
$("#target").droppable({
drop: function (event, ui) {
var offset = $('#target').offset()
var top = parseInt($(ui.draggable).css('top')) - offset.top;
var left = parseInt($(ui.draggable).css('left')) - offset.left;
$(ui.draggable).appendTo("#target");
$(ui.draggable).css({'top' : top, 'left' : left})
$(ui.draggable).animate({
top: 0,
left: 0
}, 500, function() {
// Animation complete.
});
}
});
http://jsfiddle.net/hsjvP/22/
I want to achieve something like :
$("#left").hide('slide', {direction: 'right'}, 1000)
However I do not want the div to be hidden I want it to keep up space so I want have the visibility hidden like:
$("#left").css('visibility','hidden')
Yet still achieve the same effect as above.
This is what I'd do
$parent = $('#left').parent(); //store the parent of the element in a variable
$('#left').clone() //clone the existing element
.appendTo($parent) // insert it into the current position
.css('visibility','hidden') //set it's visibility to hidden
.end().end() //target the initial element
.slideUp() //do any slide/hide/animation that you want here, the clone will always be there, just invisible
This could be horrible, but it's the only way I could think of solving the problem :)
EXAMPLE: http://jsfiddle.net/skyrim/j2RWt/4
Try this:
var $content = $("#left");
var offset = $content.offset();
$("<div></div>").css({
width: 0,
position: "absolute",
left: offset.left,
top: offset.top,
height: $content.outerHeight(),
backgroundColor: "White"
}).appendTo("body")
.animate({
width: $content.outerWidth()
}, 1000, function () {
$content.css('visibility', 'hidden');
$(this).remove();
});
EDIT
So, after learning what the actual need was (:p), this method basically place another div over the original element. I've tested it on IE...and I'll edit this with an update after I do further testing on other browsers!
EDIT
Only Chrome seems to be having an issue with getting the correct height.
Added a callback which removes the makes visibility hidden (as LEOPiC suggested) and removes the slideout div
You can do it in very simple way. There is really a nice tutorial here to animate in different direction. It will surely help you. try this
$('#left').animate({width: 'toggle'});
EXAMPLE : http://jsfiddle.net/2p3FK/2/
EDIT: One more solution, this is very simple to move the div out of window with left margin
$("#left").animate({marginLeft:'1000px'},'slow');
EXAMPLE : http://jsfiddle.net/2p3FK/1/
I'm currently extending the lavalamp plugin to work on dropdown menus but I've encountered a small problem. I need to know the offsetWidth of an element that is hidden. Now clearly this question makes no sense, rather what I'm looking for is the offsetWidth of the element were it not hidden.
Is the solution to show it, grab the width, then hide again? There must be a better way...
The width of an element that has CSS visibility: hidden is measurable. It's only when it's display: none that it's not rendered at all. So if it's certain the elements are going to be absolutely-positioned (so they don't cause a layout change when displayed), simply use css('visibility', 'hidden') to hide your element instead of hide() and you should be OK measuring the width.
Otherwise, yes, show-measure-hide does work.
The only thing I can think of is to show it (or a clone of it) to allow retrieval of the offsetWidth.
For this measurement step, just make its position absolute and its x or y value a big negative, so it will render but not be visible to the user.
You can use the following function to get the outer width of an element that is inside a hidden container.
$.fn.getHiddenOffsetWidth = function () {
// save a reference to a cloned element that can be measured
var $hiddenElement = $(this).clone().appendTo('body');
// calculate the width of the clone
var width = $hiddenElement.outerWidth();
// remove the clone from the DOM
$hiddenElement.remove();
return width;
};
You can change .outerWidth() to .offsetWidth() for your situation.
The function first clones the element, copying it to a place where it will be visible. It then retrieves the offset width and finally removes the clone. The following snippet illustrates a situation where this function would be perfect:
<style>
.container-inner {
display: none;
}
.measure-me {
width: 120px;
}
</style>
<div class="container-outer">
<div class="container-inner">
<div class="measure-me"></div>
</div>
</div>
Please be aware that if there is CSS applied to the element that changes the width of the element that won't be applied if it's a direct descendant of body, then this method won't work. So something like this will mean that the function doesn't work:
.container-outer .measure-me {
width: 100px;
}
You'll either need to:
change the specificity of the CSS selector ie. .measure-me { width: 100px; }
change the appendTo() to add the clone to a place where your CSS will also be applied to the clone. Ensure that where ever you do put it, that the element will be visible: .appendTo('.container-outer')
Again, this function assumes that the element is only hidden because it's inside a hidden container. If the element itself is display:none, you can simply add some code to make the clone visible before you retrieve it's offset width. Something like this:
$.fn.getHiddenOffsetWidth = function () {
var hiddenElement $(this)
width = 0;
// make the element measurable
hiddenElement.show();
// calculate the width of the element
width = hiddenElement.outerWidth();
// hide the element again
hiddenElement.hide();
return width;
}
This would work in a situation like this:
<style>
.measure-me {
display: none;
width: 120px;
}
</style>
<div class="container">
<div class="measure-me"></div>
</div>
Two options:
position the element outside the viewport (ex: left:-10000px)
use visibility: hidden or opacity: 0 instead of hide().
Either way will work as hiding the element but still being able to get the computed width. Be careful with Safari on thi, it's awfully fast and sometimes too fast...
Actual jQuery plugin!
Usage:
console.log('width without actual: ' + $('#hidden').width());
console.log('width with actual: ' + $('#hidden').actual('width'));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery.actual/1.0.19/jquery.actual.min.js"></script>
<div style="width: 100px; display: none;">
<div id="hidden"></div>
</div>
If you know the element to be the full width of a parent element another approach is to create a recursive method:
es5:
var getWidth;
getWidth = function($el){
return $el.offsetWidth || getWidth($el.parentElement);
}
var width = getWidth(document.getElementById('the-element'));
es6:
let getWidth
getWidth = ($el) => $el.offsetWidth || getWidth($el.parentElement)
const width = getWidth(document.getElementById('the-element'))
What I did was ;
by the time hiding that element, stored its width in its dataset.
It only will work for you if you can hide programmatically.
ie.
When Hiding ;
var elem = $("selectorOfElement");
elem.dataset.orgWidth = elem.clientWidth;
Later when getting ;
var elem = $("selectorOfElement");
var originalWidthWas = elem.dataset.orgWidth;
thats because its hidden via display: none; What ive done in the past is to make a "reciever" div which i use absolute positioning on to get it off the page. Then i load the new element into that, grab the dimensions and then remove it when im done - then remove the reciever when im done.
Another thing you can do is to not use hide(); but to instead set visibility: hidden; display: ; However this means the blank area will be rendered wherever the node is attached.
var $hiddenElement = $('#id_of_your_item').clone().css({ left: -10000, top: -10000, position: 'absolute', display: 'inline', visibility: 'visible' }).appendTo('body');
var width = parseInt($hiddenElement.outerWidth());
$hiddenElement.remove();
I try to find working function for hidden element but I realize that CSS is much complex than everyone think. There are a lot of new layout techniques in CSS3 that might not work for all previous answers like flexible box, grid, column or even element inside complex parent element.
flexibox example
I think the only sustainable & simple solution is real-time rendering. At that time, browser should give you that correct element size.
Sadly, JavaScript does not provide any direct event to notify when element is showed or hidden. However, I create some function based on DOM Attribute Modified API that will execute callback function when visibility of element is changed.
$('[selector]').onVisibleChanged(function(e, isVisible)
{
var realWidth = $('[selector]').width();
var realHeight = $('[selector]').height();
// render or adjust something
});
For more information, Please visit at my project GitHub.
https://github.com/Soul-Master/visible.event.js
demo: http://jsbin.com/ETiGIre/7
Sorry I am late to this conversation. I am surprised no one has mentioned getComputedStyle. (Note this only works if the CSS sets a width value)
Grab the element:
let yourEle = document.getElementById('this-ele-id');
and use the function:
getComputedStyle(yourEle).width
This returns a string so you will have to remove the numbers from the string.
This works even when the element's display style is set to none.
Other articles to read about this includes here at zellwk.com