Make element collapse with jQuery's animate() - javascript

I'm experimenting with an animation that makes an inline html element collapse on itself (all the elements move to the center). The way I'm approaching it is very clunky and does not work as the width of the element changes.
Here is my attempt: http://jsfiddle.net/JFVxX/1/
HTML
<p id="one">1</p> × <p id="two">2</p>
CSS
p {
display: inline;
position:relative;
}
JS
$(document).ready(function() {
$('#one').animate({
left:'+=10'
});
$('#two').animate({
right:'+=10'
});
});

Demo
To position them based on their size, ask for their size. My formula for w / 2 + 10 is valid for when there is exactly 10px between the right edge of the first element, and the left edge of the second.
<p id="three">12345</p> × <p id="four">78910</p>
$('#three').animate({
left:'+=' + ( $('#three').width() / 2 + 10 )
});
$('#four').animate({
right:'+=' + ( $('#four').width() / 2 + 10 )
});
It goes from this,
to this,

Related

Find div width that makes the text inside not wrapped

I built a CRM with some javascript to adjust the columns widths as the user wants, but I would like to have a button to automatically adjust the width of all column.
The idea is to check each div to verify if the text is wrapped and if so, increase the div width (within a given limit) or reduce in the opposite case. Something like that:
while(div.contentTextWrapped && parseInt(div.style.width)>50)
{
div.style.width=parseInt(div.style.width)-5+"px";
}
while(div.contentTextWrapped && parseInt(div.style.width)<500)
{
div.style.width=parseInt(div.style.width)+5+"px";
}
But I don't know such feature. Maybe I could set the white-space to nowrap and check the width but I need the content is not an element, or maybe I need to add a div inside and then I could check its width and compare ?
I think you have to calculate number of lines and then adjust width of object. There are a lot of ways to do that, here's one approach:
function findWidth(obj, minW, maxW) {
let lines, ref, lineHeight, wdt, dif=10, done=false;
// Get line height from myDummy
ref=document.getElementById('myDummy')
lineHeight=ref.offsetHeight;
if(lineHeight==0) return
// Assign min widht to start
wdt=minW;
obj.style.width=wdt+'px';
lines=Math.round(obj.offsetHeight/lineHeight);
while(lines>1) {
// Get current width and increase or assign max / min and exit
wdt=parseInt(obj.style.width);
if(wdt+dif>maxW) {
wdt=maxW;
done=true;
} else if(wdt<minW) {
wdt=minW;
done=true;
} else {
wdt+=dif;
}
obj.style.width=wdt+'px';
if(done) break;
lines=Math.round(obj.offsetHeight/lineHeight);
}
}
// Some tests...
findWidth(document.getElementById('myDiv1'), 25, 200);
findWidth(document.getElementById('myDiv2'), 25, 200);
findWidth(document.getElementById('myDiv3'), 25, 200);
#myDummy {
visibility: hidden;
}
div {
border: 1px solid crimson;
}
<div id="myDummy"> </div>
<div id="myDiv1">
This is some text...
</div>
<div id="myDiv2">
This is some text... with more and more and more and more and more...
</div>
<div id="myDiv3">
T...
</div>
You can try to find width of one character. then get whole text of your div and calculate total size of div:
const singleCharWidth = 3; // in px
const textLength = div.textContent.length;
div.style.width = `${textLength * singleCharWidth}px`;
If you want to read more about calculating text width you can read more about this here: Calculate text width with JavaScript

jQuery append text while maintain height and hiding previous text

I have this div, its contents are from jquery append():
Each time the text length reaches end of div width, the text will keep going and changing the div's height.
But I want to maintain div's height and hiding previous text. Also, I have a PNG with gradient and I want to put the png image to the left when jquery append detected the div has been full with text.
Expected result:
What I've been tried:
https://www.sitepoint.com/community/t/how-do-you-put-a-div-without-causing-a-new-line-break/7398/8
No Line Break With jQuery .append()
My current code (javascript):
$.post(base_url + "ajax/validate_answer", {'quiz': load, 'answer': answer}, function (data) {
/* if incorrect answer */
if (data.answer_details.correct === false)
{
$("." + current_did).css("color", "#D05555");
}
/* append text with a new word */
$(".dictionary-stack").append(' • • • <span class="' + data.next_quiz.display.dictionary.did + '">' + data.next_quiz.display.dictionary.ja_kanji + ' ' + data.next_quiz.display.dictionary.ja_roman + ' [' + data.next_quiz.display.dictionary.en_roman + ']</span>');
}
CSS to the container (.dictionary-stack):
.dictionary-stack
{
position: absolute;
bottom:100px;
width:100%;
display: block;
background:#E6E6E6;
padding: 20px;
color:#333333;
}
How I can do that?
Thanks.
My idea to solve this problem is:
on document ready compute the height and save as attribute
before append set opacity to 0
append text
after 50 milliseconds start checking the new height: while it is greater the
the original start removing (saving) the chars from the beginning of the div
The snippet (for the demo I used a different url):
var txt = $(".dictionary-stack").text();
var interval;
$(".dictionary-stack").text('a').attr('data-height', $(".dictionary-stack").outerHeight()).text(txt);
$('#myBtn').on('click', function (e) {
$.get('https://api.github.com/repositories', {'since': 364}, function (data) {
$(".dictionary-stack").css('opacity', 0);
$(".dictionary-stack").append(' • • • <span class="' +
data[0].archive_url + '">' +
data[0].archive_url + ' ' +
data[0].archive_url + ' [' +
data[0].archive_url + ']</span>');
clearTimeout(interval);
interval = setTimeout(function () {
while ($(this).outerHeight() > +$(this).attr('data-height')) {
this.textContent = this.textContent.substr(1);
}
$(this).css('opacity', 1);
}.bind($(".dictionary-stack").get(0)), 50);
});
});
.dictionary-stack
{
position: absolute;
bottom:100px;
width:100%;
display: block;
background:#E6E6E6;
padding: 20px;
color:#333333;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button type="button" id="myBtn">Load Data</button>
<div class="dictionary-stack"></div>
Without seeing your HTML/CSS, it is difficult to deliver a solution for your exact case.
As you know, your text is wrapping and that is causing the div to increase in height.
You can use some of these CSS properties to discover the correct solution for your case:
height:30px (i.e. a fixed height),
width:200% (i.e. a width that will not wrap),
overflow:hidden (to hide overflowed text when used with a fixed height)
/*
In the example below, you need the `#container` div so that you can use `overflow:hidden` on it -- to suppress the horizontal scrollbar. Without this div (and its overflow:hidden css instruction) the horizontal scrollbar will appear on the body - and that is more difficult to suppress.
*/
#container{overflow:hidden;}
#normal{background:wheat;margin-bottom:20px;}
#allowOverflow {position:relative;left:-400%;width:500%;height:25px;overflow:hidden;text-align:right;background:palegreen;}
<div id="container">
<div id="normal">
Once upon a midnight dreary, While I pondered weak and weary, Over many a quaint and curious volume of forgotten lore. While I nodded, nearly napping, suddenly there came a tapping - as of someone gently rapping - rapping on chamber door. 'Tis some visitor, I muttered, rapping on my chamber door. Merely that and nothing more.
</div>
<div id="allowOverflow">
Once upon a midnight dreary, While I pondered weak and weary, Over many a quaint and curious volume of forgotten lore. While I nodded, nearly napping, suddenly there came a tapping - as of someone gently rapping - rapping on chamber door. 'Tis some visitor, I muttered, rapping on my chamber door. Merely that and nothing more.
</div>
</div>

Insert inline element and animate shift to left

I've been trying to solve this problem for a week now and it seems basic, so maybe I'm missing something.
I want to have a div centered on the screen (or its container), and then insert a second div to the right of it, so that afterwards the two of them are centered (space on each side is equal).
Inserting the second div is not a problem, but I need the first block to slide over to where its going to be after the new block is inserted.
http://jsfiddle.net/rdbLbnw1/
.container {
width:100%;
text-align:center;
}
.inside {
border:solid 1px black;
width:100px;
height:100px;
display:inline-block;
}
$(document).ready(function() {
$("#add").click(function() {
$(".container").append("<div class='inside'></div>");
});
});
<div class="container">
<div class="inside"></div>
</div>
<input id="add" type="button" value="add"/>
Do I need to explicitly calculate where the original box is going to end up and then animate that, or is there a better way to do it?
I like your question so decide to write this:
$(document).ready(function() {
var isInAction = false;
var intNumOfBoxes = 1;
var intMargin = 10;
$containerWidth = $(".container").width();
$insideWidth = $(".inside").width();
$(".inside").css('margin-left',($containerWidth - $insideWidth - intMargin)/2 + 'px');
$("#add").click(function() {
if (!isInAction){
isInAction = true;
intNumOfBoxes +=1;
$(".current").removeClass("current");
$(".container").append("<div class='inside current'></div>");
$('.inside').animate({
'margin-left': '-=' + ($insideWidth + intMargin)/2 + 'px'
}, 300, function () {
$(".current").css('margin-left',($containerWidth + ((intNumOfBoxes - 2) * ($insideWidth + intMargin)))/2 + 'px');
$(".current").fadeIn(500,function(){
isInAction = false;
});
});
}
});
});
Also add this class in CSS:
.current {
display:none;
}
You don't need to change variables in JS code except intMargin. you can change this var to set margin between boxes.
Note: This code works fine on older browsers too and not need to support CSS3 features like transition.
Update: Some bugs like repeated clicks fixed.
Check JSFiddle Demo
First, we can animate only things that have explicit numerical values such as width, left or margin. We can't animate things like alignment (which actually use the same margin property but implicitly, never mind). So if we know width of inserted div let's just add it to our container.
1) Let's centre container itself and add transition to it
.container {
width: 102px; /* set explicit width; 102 - because of borders */
margin: auto; /* set margin to 'auto' - that will centre the div */
transition: width 0.5s;
}
2) Then increase the width when add div
$(".container").width($(".container").width() + 102);
3) But wait! If we add div to too narrow container it will be added to bottom not to right. So we need another container set to appropriate width before.
See final example on JSFiddle.
BTW, remove all line breaks and tabs from your code when you use inline-block, because it will cause spaces between your blocks.

how to set font size based on container size? [duplicate]

This question already has answers here:
Font scaling based on size of container
(41 answers)
Closed 1 year ago.
I have a container that has a % width and height, so it scales depending on external factors. I would like the font inside the container to be a constant size relative to the size of containers. Is there any good way to do this using CSS? The font-size: x% would only scale the font according to the original font size (which would be 100%).
If you want to set the font-size as a percentage of the viewport width, use the vwunit:
#mydiv { font-size: 5vw; }
The other alternative is to use SVG embedded in the HTML. It will just be a few lines. The font-size attribute to the text element will be interpreted as "user units", for instance those the viewport is defined in terms of. So if you define viewport as 0 0 100 100, then a font-size of 1 will be one one-hundredth of the size of the svg element.
And no, there is no way to do this in CSS using calculations. The problem is that percentages used for font-size, including percentages inside a calculation, are interpreted in terms of the inherited font size, not the size of the container. CSS could use a unit called bw (box-width) for this purpose, so you could say div { font-size: 5bw; }, but I've never heard this proposed.
Another js alternative:
Working Example
fontsize = function () {
var fontSize = $("#container").width() * 0.10; // 10% of container width
$("#container h1").css('font-size', fontSize);
};
$(window).resize(fontsize);
$(document).ready(fontsize);
Or as stated in torazaburo's answer you could use svg. I put together a simple example as a proof of concept:
SVG Example
<div id="container">
<svg width="100%" height="100%" viewBox="0 0 13 15">
<text x="0" y="13">X</text>
</svg>
</div>
You may be able to do this with CSS3 using calculations, however it would most likely be safer to use JavaScript.
Here is an example: http://jsfiddle.net/8TrTU/
Using JS you can change the height of the text, then simply bind this same calculation to a resize event, during resize so it scales while the user is making adjustments, or however you are allowing resizing of your elements.
I used Fittext on some of my projects and it looks like a good solution to a problem like this.
FitText makes font-sizes flexible. Use this plugin on your fluid or responsive layout to achieve scalable headlines that fill the width of a parent element.
It cannot be accomplished with css font-size
Assuming that "external factors" you are referring to could be picked up by media queries, you could use them - adjustments will likely have to be limited to a set of predefined sizes.
Here is the function:
document.body.setScaledFont = function(f) {
var s = this.offsetWidth, fs = s * f;
this.style.fontSize = fs + '%';
return this
};
Then convert all your documents child element font sizes to em's or %.
Then add something like this to your code to set the base font size.
document.body.setScaledFont(0.35);
window.onresize = function() {
document.body.setScaledFont(0.35);
}
http://jsfiddle.net/0tpvccjt/
I had a similar issue but I had to consider other issues that #apaul34208 example did not tackle. In my case;
I have a container that changed size depending on the viewport using media queries
Text inside is dynamically generated
I want to scale up as well as down
Not the most elegant of examples but it does the trick for me. Consider using throttling the window resize (https://lodash.com/)
var TextFit = function(){
var container = $('.container');
container.each(function(){
var container_width = $(this).width(),
width_offset = parseInt($(this).data('width-offset')),
font_container = $(this).find('.font-container');
if ( width_offset > 0 ) {
container_width -= width_offset;
}
font_container.each(function(){
var font_container_width = $(this).width(),
font_size = parseFloat( $(this).css('font-size') );
var diff = Math.max(container_width, font_container_width) - Math.min(container_width, font_container_width);
var diff_percentage = Math.round( ( diff / Math.max(container_width, font_container_width) ) * 100 );
if (diff_percentage !== 0){
if ( container_width > font_container_width ) {
new_font_size = font_size + Math.round( ( font_size / 100 ) * diff_percentage );
} else if ( container_width < font_container_width ) {
new_font_size = font_size - Math.round( ( font_size / 100 ) * diff_percentage );
}
}
$(this).css('font-size', new_font_size + 'px');
});
});
}
$(function(){
TextFit();
$(window).resize(function(){
TextFit();
});
});
.container {
width:341px;
height:341px;
background-color:#000;
padding:20px;
}
.font-container {
font-size:131px;
text-align:center;
color:#fff;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="container" data-width-offset="10">
<span class="font-container">£5000</span>
</div>
https://jsfiddle.net/Merch80/b8hoctfb/7/
I've given a more detailed answer of using vw with respect to specific container sizing in this answer, so I won't just repeat my answer here.
In summary, however, it is essentially a matter of factoring (or controlling) what the container size is going to be with respect to viewport, and then working out the proper vw sizing based on that for the container, taking mind of what needs to happen if something is dynamically resized.
So if you wanted a 5vw size at a container at 100% of the viewport width, then one at 75% of the viewport width you would probably want to be (5vw * .75) = 3.75vw.
If you want to scale it depending on the element width, you can use this web component:
https://github.com/pomber/full-width-text
Check the demo here:
https://pomber.github.io/full-width-text/
The usage is like this:
<full-width-text>Lorem Ipsum</full-width-text>
You can also try this pure CSS method:
font-size: calc(100% - 0.3em);

Animate element, but keep container centered

I am currently working on a custom lightbox script and need some assistance. I have the animation for the image resizing working great, but I ran into a small problem. The lightbox is shown in the middle of the user's screen, but as soon as I animate the width and height, it doesn't remain in the center of the screen, and just uses the old left and top values.
Here's the markup for the lightbox itself:
<div id="jqgal_container">
<div id="jqgal_nav_left" class="jqgal_nav"><</div>
<div id="jqgal_main">
<div id="jqgal_main_img"></div>
<div id="jqgal_footer">
<div id="jqgal_main_caption"><p></p></div>
<div id="jqgal_thumbnav_left" class="jqgal_thumbnav"><</div>
<div id="jqgal_thumbs">
<div id="jqgal_thumbs_container">
<ul></ul>
</div>
</div>
<div id="jqgal_thumbnav_right" class="jqgal_thumbnav">></div>
</div>
</div>
<div id="jqapp_nav_right" class="jqgal_nav">></div>
</div>
The image to be displayed is stored within the #jqgal_main_img as an <img /> element. I animate the width and height of #jqgal_main_img, but I also want to keep the whole container (#jqgal_container) centered on the screen.
My question is, how can I animate the width of the child element, yet still animate the top and left positions of the container respectively, so it appears to expand and grow from the centre?
The code to animate the width and height of the image container at the moment looks as follows:
_resizeToFit : function(img_width, img_height, compFunc)
{
// Calculate the width and height needed:
var req_width = img_width;
var req_height = img_height;
this._elements.mainimg.animate({ width: req_width }, {
duration: 'fast',
complete: function() {
$.jqgal._elements.footer.width(req_width);
$.jqgal._elements.mainimg.animate({ height: req_height }, 'fast', function() {
if(compFunc != undefined) { compFunc.call(this); }
});
},
step : function() {
}
});
}
Also animate the margin-left and margin-top:
"margin-left" : ($(window).width() - req_width) / 2
"margin-top" : ($(window).height() - req_height) / 2
If the elements CSS position is 'absolute', change margin-top / margin-left to top and left.
EDIT:
At the end of the whole 'animate' string, add
.parent().animate({
"margin-left" : )($(window).width() - req_width) / 2) + this._elements.mainimg.parent().scrollLeft(),
"margin-top" : (($(window).height() - req_height) / 2 ) + this._elements.mainimg.parent().scrollTop()
}}
Or in the callback function:
$(this).parent().animate({...});
For ease, it might be best to set variables holding the elements too...
$this = this._elements.mainimg;
$parent = $this.parent();
Then the parent animate() data would look like this:
"margin-left" : )($(window).width() - req_width) / 2) + $parent.scrollLeft(),
"margin-top" : (($(window).height() - req_height) / 2 ) + $parent.scrollTop()
}}
Which is a little easier to read!
i think you should Animate too the margin's and left distance.

Categories

Resources