GithubHelp home page GithubHelp logo

rikschennink / fitty Goto Github PK

View Code? Open in Web Editor NEW
3.7K 42.0 211.0 798 KB

✨ Makes text fit perfectly

Home Page: https://rikschennink.github.io/fitty/

License: MIT License

JavaScript 100.00%
font-size resize scale pixel text responsive flexible fluid javascript

fitty's Introduction

Fitty, Snugly text resizing

Scales up (or down) text so it fits perfectly to its parent container.

Ideal for flexible and responsive websites.

Visit PQINA.nl for other useful Web Components

License: MIT npm version npm


Buy me a Coffee / Dev updates on Twitter


Features

  • No dependencies
  • Easy setup
  • Optimal performance by grouping DOM read and write operations
  • Works with WebFonts (see example below)
  • Min and max font sizes
  • Support for MultiLine
  • Auto update when viewport changes
  • Monitors element subtree and updates accordingly

Installation

Install from npm:

npm install fitty --save

Or download dist/fitty.min.js and include the script on your page like shown below.

Usage

Run fitty like shown below and pass an element reference or a querySelector. For best performance include the script just before the closing </body> element.

<div id="my-element">Hello World</div>

<script src="fitty.min.js"></script>
<script>
    fitty('#my-element');
</script>

The following options are available to pass to the fitty method.

Option Default Description
minSize 16 The minimum font size in pixels
maxSize 512 The maximum font size in pixels
multiLine true Wrap lines when using minimum font size.
observeMutations MutationObserverInit Rescale when element contents is altered. Is set to false when MutationObserver is not supported. Pass a custom MutationObserverInit config to optimize monitoring based on your project. By default contains the MutationObserverInit configuration below or false based on browser support.

Default MutationObserverInit configuration:

{
  subtree: true,
  childList: true,
  characterData: true
}

You can pass custom arguments like this:

fitty('#my-element', {
    minSize: 12,
    maxSize: 300,
});

The fitty function returns a single or multiple Fitty instances depending on how it's called. If you pass a query selector it will return an array of Fitty instances, if you pass a single element reference you'll receive back a single Fitty instance.

Method Description
fit(options) Force a redraw of the current fitty element
freeze() No longer update this fitty on changes
unfreeze() Resume updates to this fitty
unsubscribe() Remove the fitty element from the redraw loop and restore it to its original state
Properties Description
element Reference to the related element
var fitties = fitty('.fit');

// get element reference of first fitty
var myFittyElement = fitties[0].element;

// force refit
fitties[0].fit();

// force synchronous refit
fitties[0].fit({ sync: true });

// stop updating this fitty and restore to original state
fitties[0].unsubscribe();

Fitty dispatches an event named "fit" when a fitty is fitted.

Event Description
"fit" Fired when the element has been fitted to the parent container.

The detail property of the event contains an object which exposes the font size oldValue the newValue and the scaleFactor.

myFittyElement.addEventListener('fit', function (e) {
    // log the detail property to the console
    console.log(e.detail);
});

The fitty function itself also exposes some static options and methods:

Option Default Description
fitty.observeWindow true Listen to the "resize" and "orientationchange" event on the window object and update fitties accordingly.
fitty.observeWindowDelay 100 Redraw debounce delay in milliseconds for when above events are triggered.
Method Description
fitty.fitAll(options) Refits all fitty instances to match their parent containers. Essentially a request to redraw all fitties. The options object is passed to fitty instance fit() method.

Performance

For optimal performance add a CSS selector to your stylesheet that sets the elements that will be resized to have white-space:nowrap and display:inline-block. If not, Fitty will detect this and will have to restyle the elements automatically, resulting in a slight performance penalty.

Suppose all elements that you apply fitty to are assigned the fit class name, add the following CSS selector to your stylesheet:

.fit {
    display: inline-block;
    white-space: nowrap;
}

Should you only want to do this when JavaScript is available, add the following to the <head> of your web page.

<script>
    document.documentElement.classList.add('js');
</script>

And change the CSS selector to:

.js .fit {
    display: inline-block;
    white-space: nowrap;
}

Do Not Upscale Text

Fitty calculates the difference in width between the text container and its parent container. If you use CSS to set the width of the text container to be equal to the parent container it won't scale the text.

This could be achieved by forcing the text container to be a block level element with display: block !important or setting its width to 100% with width: 100%.

Custom Fonts

Fitty does not concern itself with custom fonts. But it will be important to redraw Fitty text after a custom font has loaded (as previous measurements are probably incorrect at that point).

If you need to use fitty on browsers that don't have CSS Font Loading support (Edge and Internet Explorer) you can use the fantastic FontFaceObserver by Bram Stein to detect when your custom fonts have loaded.

See an example custom font implementation below. This assumes fitty has already been called on all elements with class name fit.

<style>
    /* Only set the custom font if it's loaded and ready */
    .fonts-loaded .fit {
        font-family: 'Oswald', serif;
    }
</style>
<script>
    (function () {
        // no promise support (<=IE11)
        if (!('Promise' in window)) {
            return;
        }

        // called when all fonts loaded
        function redrawFitty() {
            document.documentElement.classList.add('fonts-loaded');
            fitty.fitAll();
        }

        // CSS Font Loading API
        function native() {
            // load our custom Oswald font
            var fontOswald = new FontFace('Oswald', 'url(assets/oswald.woff2)', {
                style: 'normal',
                weight: '400',
            });
            document.fonts.add(fontOswald);
            fontOswald.load();

            // if all fonts loaded redraw fitty
            document.fonts.ready.then(redrawFitty);
        }

        // FontFaceObserver
        function fallback() {
            var style = document.createElement('style');
            style.textContent =
                '@font-face { font-family: Oswald; src: url(assets/oswald.woff2) format("woff2");}';
            document.head.appendChild(style);

            var s = document.createElement('script');
            s.src =
                'https://cdnjs.cloudflare.com/ajax/libs/fontfaceobserver/2.0.13/fontfaceobserver.standalone.js';
            s.onload = function () {
                new FontFaceObserver('Oswald').load().then(redrawFitty);
            };
            document.body.appendChild(s);
        }

        // Does the current browser support the CSS Font Loading API?
        if ('fonts' in document) {
            native();
        } else {
            fallback();
        }
    })();
</script>

Notes

  • Will not work if the fitty element is not part of the DOM.

  • If the parent element of the fitty element has horizontal padding the width calculation will be incorrect. You can fix this by wrapping the fitty element in another element.

<!-- Problems -->
<div style="padding-left:100px">
    <h1 class="fit">I'm a wonderful heading</h1>
</div>
<!-- No more problems -->
<div style="padding-left:100px">
    <div><h1 class="fit">I'm a wonderful heading</h1></div>
</div>

Tested

  • Modern browsers
  • IE 10+

Note that IE will require CustomEvent polyfill: https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent/CustomEvent#Polyfill

IE10 will require a polyfill for Object.assign: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign

Versioning

Versioning follows Semver.

License

MIT

fitty's People

Contributors

byeokim avatar chou0728 avatar fneco avatar iyel avatar kinging123 avatar kubajastrz avatar manuelandro avatar maxfriedmann avatar miguelcobain avatar rikschennink avatar tiffanylphan avatar tomconroy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

fitty's Issues

Not working with Links

Hi,

i have something like that:

Text to fit

But it is not working. It works only with inside the div. but than is not styling the link :)

Wait until fonts loaded through `@font-face` have been applied

When loading an external font through @font-face, the font size will already have been calculated based on the fallback font (e.g. Times New Roman). When the external font is applied, the font size isn't being recalculated.

Possible solution is to see if the proper fonts have been loaded and applied (using Fontfaceobserver perhaps). Or you might consider this out of scope and put in the instructions that people have to do this yourself.

Fit to container height

Awesome work on this library! I'm looking for something similar but that scales the font size of multi-line text to always fit the container height rather than width. Any thoughts on adding a height option?

Strange rendering on Firefox when using Grid View

I'm using Fitty on a couple of pages. Didn't notice any problems when working locally, but once on a live server I started to get unexpected results. Essentially what I'm seeing is everything as expected on first load, but when moving between pages using Fitty, or refreshing the page, the calculations are wrong (font size too big), most noticeable in FireFox for me. When this happens, resizing the window triggers a recalculation, the page then renders as expected.

Live example https://gigs.smth.uk/

.unsubscribe() should consider all changed inline-styles

At the moment the unsubscribe() only removes the font-size from the style attribute, leaving white-space: nowrap and display: inline-block in place.

Best case would be, if the plugin remembers, if it added one or both of these styles and remove them accordingly when unsubscribing to accurately reset the styling to what it was before applying fitty()

Improve handling of selector input

When passing a querySelector to the fitty method Fitty currently converts it to an array of elements then exists the method and calls fitty for each node. Each instance requests a redraw pushing away the initial redraw moment.

Parsing all nodes and then requesting a single redraw possibly improves performance.

Possible to fit the paragraphs?

Hello,

Is it possible to fit the paragraphs? I notice that it sets nowrap on the element that makes half of the paragraph hide under the div. Basically i am trying to achieve how facebook does to its posts while displaying. Less content posts are bigger and more content is to a minimum. Or should i achieve this on my own and set fitty based on content length? Just making sure i'm not missing any api call that would do this without extra code.

fitty_module is not a function

Hi

I'm getting TypeError: fitty_module is not a function when using Fitty with Rollup.

I'm doing this:

import fitty from 'fitty'

fitty('.my-element');

My Rollup config is essentially this.

What am I doing wrong?

Copy and pasting breaks width limit

I have checked out your demo on the fitty page and I came across some bugs while copy and pasting:

  • when pasting one character (e.g. "a" of wizard) over and over again: the "a"s keep their size and slowly begin to break the width length
  • when pasting more than one character, all characters won't even resize and the width limit will be broken instantly

Tested on Chrome 59.0.3071.115 (64-Bit) and Safari Version 10.1.1 (12603.2.4) on macOS 10.12.5 (16F73).

Provide uncompressed transpiled version of fitty as part of distribution

Currently, if I install fitty from npm and try to import like this

import fitty from 'fitty'

then the fitty.js file will be imported, because it is specified as the main script.

However, it is very likely that you'll get a JavaScript error, because fitty.js is written in ES6. Tools like vue-cli or create-react-app generate such Webpack config that applies Babel transpilation only to application source. In order to fix the import, I have to write import fitty from 'fitty/fitty.min.js', which is less obvious and makes IDEs life more difficult.

It would be nice if the current fitty.js is moved to e.g. src folder, and two transpiled distribution versions of the library (uncompressed and compressed) are provided.

Can't seem to get fitty to work in a vue.js component.

I am trying to get fitty to run in vue js component and have imported the plugin, and am then running the method on the mounted() lifecycle hook. But its returning the reference error: fitty is not defined error.

Here is my code. do you know how I could get this working?

	<script>

		import Fitty from 'fitty'

		export default {
			name:'GhostHeadline',
			beforeCreate() {  

	        },
			props: {
				headline: {
					required:true,
					type:String
				}
			},
			mounted() {
				this.resizeHeadline();
			},
			methods: {
				resizeHeadline() {
					const headline = this.$refs.headline
					fitty(headline, {
						maxSize:1000
					})
				}
			}
		}
	</script>

RFE: support array/element list as fitty() argument

fitty() current accepts a selector or single element as its first argument. "It would be really cool" if it also accepted any container which has a forEach method, e.g. an array or querySelectorAll() result, which fitty could then internally transform into an array for its own use.

Something along the lines of (untested):

  function fitty(target, options = {}) {
    if('string' === typeof target){
        return fittyCreate( toArray( document.querySelectorAll(target) ), options);
    }else if('function' === typeof target.forEach){
        const a = [];
        target.forEach((x)=>a.push(x));
        return fittyCreate(a, options);
        // perhaps simply toArray(target) would do the trick?
    }else{
        return  fittyCreate([target], options)[0];
    }
  }

How to listen for "fit" event

This may not be the right place to ask, but how do I listen for the fit event? In the docs it says, "Fitty dispatches an event named "fit" when a fitty is fitted."

How to handle absolute positioned grandparents

I'm using the Gridstack library for creating draggable, resizable, responsive layouts. One of the layouts contains just a number with a header. Unfortunately gridstack has styles attached to each grid item that are absolutely positioned. It looks like these styles mess with fitty when calculating the size. I copied the styles over into a CodeSandbox that reproduces the problem. If a single digit number is displayed, the content will scroll and not fit the size of the box.

CodeSandbox: https://codesandbox.io/s/admiring-brattain-kd4h2

`freeze`/`unfreeze` functions are not defined in returned object

Hi, I wanted to mention that the object returned from the call to fitty contains the element, fit, and unsubscribe properties, but no feeze/unfreeze as stated in the docs. Is that intentional? What I'm trying to achieve is suspending updates, but not resetting the element to its original font size.

Add TypeScript declarations

Thinking about turning this into an Angular directive. The first step to that is to create TypeScript declaration files.

Not working with letter-spacing

Hi, thanks for this nice little plugin, could be very usefull.

Just one problem I am struggeling with, is that it seems not working if letter-spacing is used on the text element.

You can test it in my example with custom fonts and parent element with padding.

https://jsfiddle.net/rxmmpodf/19/

Behavior when loaded with node is confusing

If someone tries to require the module from Node (like in a REPL below)

$ node 
> require('fitty')
{ default: undefined }
> require('fitty').default
undefined
> require('fitty').default()
TypeError: require(...).default is not a function

It isn't clear what went wrong. I'd really recommend throwing an error or at least logging to stderr when this is attempted, at this point in your code:

  // no window, early exit
  if (!w) {
    // throw or log error here please
    return;
  }

It was really confusing to me when, separately, I wasn't able to get the import working as expected.

Oh, and thanks for writing fitty!

Not fitting, only uses max font size

I'm using this inside of a vue directive to try and fix text to it's container, however it does not seem to actually do anything except set the font size to the max I allow.

Watching the fit event shows no change even when I manually trigger the fit() function on the element.

The relevant markup looks like:

<div> <!-- div1 max-width = 150px -->
  <div> <!-- div2 no styling -->
    <span>Some long text goes here</span> <!-- The element that is fitty() -->
  </div>
</div>

The span that I am using fitty() on it larger than div 2 since the text causes it to overflow, which seems to be what fitty expects?

Other Info:

  • The text element is not forced to be the same size as the parent
  • The parent has no padding
  • Even when changing the text, the font-size is never changed by fitty when it triggers fit from the dom change
  • If I manually constrain the parent container by setting it's max-width, and then calling fit(), it still does not change the text's size to anything but the max

image

Scaling multiline text

I think this is a feature request, but perhaps there is already a way to do it. I'd like to have multiple words on separate lines, scaled equally, so that the longest word fills the width. I guess what this would mean is comparing the font size of each word, then applying the smallest to all of them?

fitty() ignores targets and parents padding

In this case I want to fitty() a B tag inside a H1 tag - but I am certain that this behavior is not only in this case. Here you can see how the H1 is set up with padding used to "guide" how and where the text will break into a new line.
beforefitty
After applying fitty I expected the B tag to be fittet into the space available inside the padding. Instead it has almost exactly the outer width of the parent, ignoring the parents and own padding for the calculation of the available space.
afterfitty

Edit: I just realized that it is the padding AND margin it seems.

When restrained by maxSize, the text doesn't obey text-align: center

I thought a comment would be perhaps appropriate for this case -- perhaps in the Notes section of the README. Since display: inline-block is being automatically applied to fitty'ed elements (when not already applied in the CSS), text-align: center must br applied to a container, rather than to the jiffy'ed element, if one desires the jiffy'ed element to be centered when maxSize restricts the text from being the same width as its container.

Fitty on bootstrap carousel , hidden text issue

Hi,
I noticed when used in a bootstrap carousel, if I have 2 slides with texts, only the visible slide will have the text "fittified".

In this pen, if the first slide is marked as active, the everything works as planned for the first slide but the second slides text is not "fittified"

In this pen if the active slide is switched to slide 2, then slide 2 is now "fittified", but slide 1 is not.

Just wondering how I can work around this?

Thanks!

Demo seems broken

Hi, I'm new to fitty, but is this intended demo behavior?

screen shot 2017-07-31 at 7 59 22 am

Mac OSX 10.12.5
Chrome 59.0.3071.115
No output in the console

How to be sure that redraw is finished

I have some issues with fitty.
When i call `fitty('selector')' in the rest of the code, fitty is sometimes still calculating/computing the font-size.

So i do:

async() => {
    fitty('selector')
    await wait(50) // return a promise which is resolved 50ms later
    // some code need to be sure that font-size computing is finish
}

Have you a safer solution ?

Thanks

Issues with Safari

Thank you for sharing this cool library. But fitty seems not to work when using Safari and @font-face together. If you use an own font with fitty, it sets the minimal font-size and doesn't fit it to it's div parent's width. In Safari it only works when using a standard font like Arial.

fit text to parent height

#9
I have implemented a solution for this, but don't have the ability to publish to a branch on this repo. What is the proper way to make a pull request for this?
P.S. I've never contributed to an open source project before so I'm a bit of a noob.

Does not work for wrapped text

If a minimum font size is set, and the container div is made small enough so that the text is forced to wrap, the wrapped text does not increase in size to fill the width of the container.

Suggestion for an enhancement: shrink-only-flag

I have a situation, where I want to make the text fit it's container only if it doesn't fit. If the text doesn't overlap it's parent's boundaries, I don't want anything to happen.

First I tried to achieve this by calculating the current font-size and setting it as max-option for Fitty. However, given our use case, this turned out to cause more issues.

Even when no resizing should have happened, Fitty would explicitly declare the font-size in the element's style-attribute, which effectively prevented any other changes to the element's size (from CSS cascading, for example).

I ended up forking Fitty for our use-case and modifying the code so that it doesn't set font-size-style unless some modification is necessary and it only does the shrinking, never enlarging the text.

So, my suggestion is to enhance Fitty so that

  1. Option flag to only do shrinking / only do enlarging
  2. Option flag or altering default behavior so that the font-size isn't set when it's not necessary

What do you think?

originalStyle enforces old CSS after external property changes

Fitty sets the element's CSS properties every time after a rerender, based on the originlStyle variable. This means that if, for example, we change the color of the text after fitty was first initialized, the changed color will be reset to the original color the element had.

Steps to reproduce:

  1. Initialize fitty on an element
  2. Change the CSS color of the element, via JS
  3. Change the contents of the element to re-fit

This is a bug in applications where fitty is used to fit dynamically user-typed content, i.e. when we want to allow the user to change the color of the element before changing the text.

Code in question:

fitty/src/fitty.js

Lines 144 to 147 in 7c2d492

if (!f.originalStyle) f.originalStyle = f.element.getAttribute('style') || '';
// set the new style to the original style plus the fitty styles
f.element.style.cssText = `${f.originalStyle};white-space:${f.whiteSpace};display:${f.display};font-size:${f.currentFontSize}px`;

Proposed fixes:

  1. Instead of setting f.originalStyle to the element's style once, parse the element's current CSS properties and only modify the ones we need to modify (white-space, font-size, etc...)
  2. Instead of changing the style by setting element.style.cssText to a string, change each property separately (element.style.whiteSpace=..., etc...) - I favor this option the most
  3. Allow the user to pass as a setting, a new CSS attribute after initialization:
f = fitty('...', {...});
// At some point
f.setCSS('color', '#ff0000');

Please let me know which option you like best, and I would gladly make a pull request with the proposed change :)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.