Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Big rework, don't split on lowercase words #2

Closed
wants to merge 4 commits into from

Conversation

danielpost
Copy link

@danielpost danielpost commented May 28, 2017

Hey guys,

I wanted to add the ability to not break on lowercase words (as mentioned in your article), and ended up with a pretty big structural overhaul of the entire plugin. Here's what I added:

  • No more splitting on lowercase words
    If the last word before the split starts with a lowercase letter and the word after that doesn't, the width will be increased to fit the next word as well. This was my initial idea and kind of prompted the rest.
  • UMD wrapper and scoped variables and functions
  • Ability to pass multiple options to the plugin
    Right now only candidates is useful, but it might be nice to eventually add more options, and this creates an easy way to add new options.
  • JSDoc comments for each function
  • Improved structure and comments
    I changed the structure to be more consistent with a regular vanilla JavaScript plugin, and added or improved some comments to hopefully improve clarity.

Initialize like this:

textBalancer.init({
    candidates: ['.headline']
});

I realize this is a pretty major (and opinionated) overhaul and I would love to hear your thoughts/suggestions/ideas!

Best,
Daniel

Pretty big rework of the plugin. Details in the PR.
@lode
Copy link

lode commented Jun 3, 2017

I'd love to use this plugin as well, but then for another language (Dutch). Not splitting after lowercase words is a bit tricky because in Dutch the words are mostly not capitalized. It would be great to make this feature an optional one.

Btw, maybe it is easier if there's separate PRs for each change, or otherwise at least separate commits. I found it hard to read what changed with this PR and how the lowercase-check affected generic usage. And I guess reviewing is also tricky this way.

@danielpost
Copy link
Author

@lode Great idea, I didn't even think about that even though I'm Dutch myself. I'll add it as an option. (quick side note: what site are you planning to use it for? Just curious!).

As for it being hard to read and review, good point. I'll give an overview of what changed and how it changed, that should hopefully help both with understanding and reviewing. As for how it changed generic usage, the only thing that changed is that if after balancing the text the plugin finds that the last word on the first line is lowercase and the word after that isn't, it increases the width of the wrapper to fit both words on the same line.

Overview of changes:

  • I used this boilerplate.
  • Added a UMD wrapper to support RequireJS, Node and Browserify (untested, but it's a standard wrapper).
  • Moved everything out of global scope.
  • I made sure all functions are declared before they are called (mostly because my linter wanted me to but I think it's also good practice in general).
  • Added JSDoc to all functions and cleaned up comments in general.

Individual functions:

  • extend(). Merges two or more objects. It's used to merge the options passed by the user on init() with the default options.
  • debounce(). Same as before, helper function to add a delay to invoking functions on for example resizing the window.
  • createSelectors(). Same as before, return DOM objects of all the selectors passed by the user.
  • elementIsMultipleLines(). Same as before, checks if the element being balanced has multiple lines.
  • getBreakingWords(). Returns an array consisting of the last word of the first, first word of the second line and the word number of the first word of the second line (eg. "7" if it's the 7th word in the text). This is used to get the new width of the wrapper in case of a lowercase word.
  • isLowercaseWord(). Super simple, checks if a word starts with a lowercase letter.
  • getNewWidth(). Uses the number from getBreakingWords() to figure out the new width of the element so that the first word of the second line fits on the first line as well.
  • squeezeContainer(). Same as before, except after splitting it does the lowercase check and sets the new width using getNewWidth() if necessary.
  • balanceText(). Main function called by init to squeeze the containers.
  • textBalancer.init(). Main initialisation method.

Sorry for the wall of text, but I hope this helps! Let me know if you have any questions.

@danielpost
Copy link
Author

danielpost commented Jun 3, 2017

@lode It's an option now, so if you wanted to avoid splitting on lowercase words you'd do this:

textBalancer.init({
    candidates: ['.headline'],
    splitOnLowercase: false
});

In your case you don't need the option since it's disabled by default.

Copy link
Contributor

@mbeswetherick mbeswetherick left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@danielpost things are looking good!

The two biggest things I want to see from this refactor are that text-balancer is backwards compatible, and that it's easily testable.

As far as the backwards compatibility, what do you think about reworking this so someone doesn't have to call init() immediately, but could call balanceText()?

What do you think? I could totally be missing something, these are just some initial thoughts :^)

element.style.maxWidth = Math.ceil(topRange + 1) + 'px';

if (!settings.splitOnLowercase) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should be defensive here and check to see if settings exists first! Don't want this to break if someone doesn't pass in settings :^)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That setting is passed in by default as true so it should be fine! I'll take a look at the other stuff soon.

@jamiemccarthy
Copy link

I really like this PR!

And today I learned about inline-block + offsetWidth, a useful combination!

Unfortunately this doesn't help news sites (like the one I work for) that use ordinary sentence case instead of capitalizing most words. But I'd still like to see this approved and merged, it takes the library in a very interesting new direction.

As a next step, I might like to experiment with several things:

  • Before expanding width to fit the second word, try inserting a newline before the first (lowercase) word, and see whether the result fits. Instead of changing these:
Randal Quarles Confirmed as
Federal Reserve Governor

Tropical Storm Nate Expected to
Hit Gulf Coast as a Hurricane

Amid Promises of Aid, a
Puerto Rico Still in Ruins

to:

Randal Quarles Confirmed as Federal
Reserve Governor

Tropical Storm Nate Expected to Hit
Gulf Coast as a Hurricane

Amid Promises of Aid, a Puerto
Rico Still in Ruins

we could see whether these fit:

Randal Quarles Confirmed
as Federal Reserve Governor

Tropical Storm Nate Expected
to Hit Gulf Coast as a Hurricane

Amid Promises of Aid,
a Puerto Rico Still in Ruins

(Perhaps simultaneously expanding the width by a small fixed amount would make this type of change more likely to succeed with little negative visual effect.)

  • Could we iterate beyond line 2, to keep this effect going for long headlines? Obviously once lines 1 and 2 are set, changing the width for additional lines is problematic. So I think after line 2 we should only try moving words around by attempting to add newlines.

  • Rather than identifying only lowercase-uppercase word pairs as significant, maybe we could look for punctuation-space as a more important word break signifier. Period-space seems most likely to produce a good break, followed maybe by colon-space, then comma-space. For example, this:

Puerto Rico Makes Crucial
Drugs. Now, Shortages Loom.

is not as readable as this:

Puerto Rico Makes Crucial Drugs.
Now, Shortages Loom.

even if it has to go a little wider.

  • Rather than going by uppercase/lowercase, perhaps a hard-coded list of common English words that begin phrases could be kept for comparison. Words like in and for seem good candidates for this: pausing after a preposition almost always interrupts flow.

  • On the other hand, I'm honestly not sure whether a and the make the flow of reading smoother at the end or the beginning of a line. Is there any research on this we should consult?

  • In Fractional balancing; simplification #3 , I've proposed several changes, notably fractional "backing off" from the minimal width. My team feels this allows presenting a more-visually-pleasing balance between tidy individual headlines, and varying headline width down the page. I'd be happy to try to incorporate that once this PR is merged. I did remove textElementIsMultipleLines for performance reasons, but if it turns out to be necessary to synthesize the changes, it could of course be added back.

@danielpost
Copy link
Author

@jamiemccarthy Is this something that would still interest you? I kind of forgot about it and this library seems mostly abandoned so I'm considering developing a modern fork.

@danielpost danielpost closed this Jan 31, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants