How to prevent long words from breaking my div?

2019-01-01 16:46发布

Ever since switching from TABLE-layout to DIV-layout, one common problem remains:

PROBLEM: you fill your DIV with dynamic text and inevitably there is a super-long word that extends over the edge of your div column and makes your site look unprofessional.

RETRO-WHINING: This never happened with table layouts. A table cell will always nicely expand to the width of the longest word.

SEVERITY: I see this problem on even the most major sites, especially on German sites where even common words such as "speed limit" are very long ("Geschwindigkeitsbegrenzung").

Does anyone have a workable solution to this?

标签: css layout html
26条回答
梦寄多情
2楼-- · 2019-01-01 17:05

Update: Handling this in CSS is wonderfully simple and low overhead, but you have no control over where breaks occur when they do. That's fine if you don't care, or your data has long alphanumeric runs without any natural breaks. We had lots of long file paths, URLs, and phone numbers, all of which have places it's significantly better to break at than others.

Our solution was to first use a regex replacement to put a zero-width space (​) after every 15 (say) characters that aren't whitespace or one of the special characters where we'd prefer breaks. We then do another replacement to put a zero-width space after those special characters.

Zero-width spaces are nice, because they aren't ever visible on screen; shy hyphens were confusing when they showed, because the data has significant hyphens. Zero-width spaces also aren't included when you copy text out of the browser.

The special break characters we're currently using are period, forward slash, backslash, comma, underscore, @, |, and hyphen. You wouldn't think you'd need do anything to encourage breaking after hyphens, but Firefox (3.6 and 4 at least) doesn't break by itself at hyphens surrounded by numbers (like phone numbers).

We also wanted to control the number of characters between artificial breaks, based on the layout space available. That meant that the regex to match long non-breaking runs needed to be dynamic. This gets called a lot, and we didn't want to be creating the same identical regexes over and over for performance reasons, so we used a simple regex cache, keyed by the regex expression and its flags.

Here's the code; you'd probably namespace the functions in a utility package:

makeWrappable = function(str, position)
{
    if (!str)
        return '';
    position = position || 15; // default to breaking after 15 chars
    // matches every requested number of chars that's not whitespace or one of the special chars defined below
    var longRunsRegex = cachedRegex('([^\\s\\.\/\\,_@\\|-]{' + position + '})(?=[^\\s\\.\/\\,_@\\|-])', 'g');
    return str
                .replace(longRunsRegex, '$1​') // put a zero-width space every requested number of chars that's not whitespace or a special char
                .replace(makeWrappable.SPECIAL_CHARS_REGEX, '$1​'); // and one after special chars we want to allow breaking after
};
makeWrappable.SPECIAL_CHARS_REGEX = /([\.\/\\,_@\|-])/g; // period, forward slash, backslash, comma, underscore, @, |, hyphen


cachedRegex = function(reString, reFlags)
{
    var key = reString + (reFlags ? ':::' + reFlags : '');
    if (!cachedRegex.cache[key])
        cachedRegex.cache[key] = new RegExp(reString, reFlags);
    return cachedRegex.cache[key];
};
cachedRegex.cache = {};

Test like this:

makeWrappable('12345678901234567890 12345678901234567890 1234567890/1234567890')

Update 2: It appears that zero-width spaces in fact are included in copied text in at least some circumstances, you just can't see them. Obviously, encouraging people to copy text with hidden characters in it is an invitation to have data like that entered into other programs or systems, even your own, where it may cause problems. For instance, if it ends up in a database, searches against it may fail, and search strings like this are likely to fail too. Using arrow keys to move through data like this requires (rightly) an extra keypress to move across the character you can't see, somewhat bizarre for users if they notice.

In a closed system, you can filter that character out on input to protect yourself, but that doesn't help other programs and systems.

All told, this technique works well, but I'm not certain what the best choice of break-causing character would be.

Update 3: Having this character end up in data is no longer a theoretical possibility, it's an observed problem. Users submit data copied off the screen, it gets saved in the db, searches break, things sort weirdly etc..

We did two things:

  1. Wrote a utility to remove them from all columns of all tables in all datasources for this app.
  2. Added filtering to remove it to our standard string input processor, so it's gone by the time any code sees it.

This works well, as does the technique itself, but it's a cautionary tale.

Update 4: We're using this in a context where the data fed to this may be HTML escaped. Under the right circumstances, it can insert zero-width spaces in the middle of HTML entities, with funky results.

Fix was to add ampersand to the list of characters we don't break on, like this:

var longRunsRegex = cachedRegex('([^&\\s\\.\/\\,_@\\|-]{' + position + '})(?=[^&\\s\\.\/\\,_@\\|-])', 'g');
查看更多
梦醉为红颜
3楼-- · 2019-01-01 17:06

I just found out about hyphenator from this question. That might solve the problem.

查看更多
情到深处是孤独
4楼-- · 2019-01-01 17:08

Two fixes:

  1. overflow:scroll -- this makes sure your content can be seen at the cost of design (scrollbars are ugly)
  2. overflow:hidden -- just cuts off any overflow. It means people can't read the content though.

If (in the SO example) you want to stop it overlapping the padding, you'd probably have to create another div, inside the padding, to hold your content.

Edit: As the other answers state, there are a variety of methods for truncating the words, be that working out the render width on the client side (never attempt to do this server-side as it will never work reliably, cross platform) through JS and inserting break-characters, or using non-standard and/or wildly incompatible CSS tags (word-wrap: break-word doesn't appear to work in Firefox).

You will always need an overflow descriptor though. If your div contains another too-wide block-type piece of content (image, table, etc), you'll need overflow to make it not destroy the layout/design.

So by all means use another method (or a combination of them) but remember to add overflow too so you cover all browsers.

Edit 2 (to address your comment below):

Start off using the CSS overflow property isn't perfect but it stops designs breaking. Apply overflow:hidden first. Remember that overflow might not break on padding so either nest divs or use a border (whatever works best for you).

This will hide overflowing content and therefore you might lose meaning. You could use a scrollbar (using overflow:auto or overflow:scroll instead of overflow:hidden) but depending on the dimensions of the div, and your design, this might not look or work great.

To fix it, we can use JS to pull things back and do some form of automated truncation. I was half-way through writing out some pseudo code for you but it gets seriously complicated about half-way through. If you need to show as much as possible, give hyphenator a look in (as mentioned below).

Just be aware that this comes at the cost of user's CPUs. It could result in pages taking a long time to load and/or resize.

查看更多
梦醉为红颜
5楼-- · 2019-01-01 17:08

Use the style word-break:break-all;. I know it works on tables.

查看更多
不流泪的眼
6楼-- · 2019-01-01 17:09

For me on a div without fixed size and with dynamic content it worked using:

display:table;
word-break:break-all;
查看更多
冷夜・残月
7楼-- · 2019-01-01 17:09

The solution I usually use for this problem is to set 2 different css rules for IE and other browsers:

word-wrap: break-word;

woks perfect in IE, but word-wrap is not a standard CSS property. It's a Microsoft specific property and doesn't work in Firefox.

For Firefox, the best thing to do using only CSS is to set the rule

overflow: hidden;

for the element that contains the text you want to wrap. It doesn't wrap the text, but hide the part of text that go over the limit of the container. It can be a nice solution if is not essential for you to display all the text (i.e. if the text is inside an <a> tag)

查看更多
登录 后发表回答