用JavaScript包装文本

时间:2013-01-23 16:41:28

标签: javascript jquery word-wrap

我是JavaScript和jQuery的新手。

我在JavaScript中有一个名为str的变量,它包含非常长的文本,例如

"A quick brown fox jumps over a lazy dog". 

我希望将其包装并通过在正确的位置插入正确的str\n标记将其分配给同一变量br/

我不想使用CSS等。您能否告诉我如何使用JavaScript中的正确函数来执行此操作,该函数将str并返回正确的格式化文本?

类似的东西:

str = somefunction(str, maxchar);

我尝试了很多,但遗憾的是没有任何东西按照我希望的方式出现! :(

非常感谢任何帮助...

12 个答案:

答案 0 :(得分:13)

尽管这个问题已经很久了,但是到目前为止,提供的许多解决方案比必需的更为复杂和昂贵,正如user2257198指出的那样-用短的单行正则表达式是完全可以解决的。

但是我发现他的解决方案存在一些问题,包括:在最大宽度之后而不是之前包装之后,中断字符类中未明确包含的字符,不考虑导致段落开始的现有换行字符中线切碎。

这促使我编写了自己的解决方案:

// Static Width (Plain Regex)
const wrap = (s, w) => s.replace(
    /(?![^\n]{1,32}$)([^\n]{1,32})\s/g, '$1\n'
);

// Dynamic Width (Build Regex)
const wrap = (s, w) => s.replace(
    new RegExp(`(?![^\\n]{1,${w}}$)([^\\n]{1,${w}})\\s`, 'g'), '$1\n'
);

奖金功能

  • 处理不是换行符的任何字符(例如代码)。
  • 正确处理现有换行符(例如段落)。
  • 防止将空格推到换行符的开头。
  • 防止在字符串末尾添加不必要的换行符。

说明

主要概念是简单地查找不包含换行符[^\n]且不超过所需长度(例如32 {1,32})的字符的连续序列。通过在字符类中使用否定^,它可以允许得多,避免了标点符号之类的遗漏,否则这些标点必须明确添加:

str.replace(/([^\n]{1,32})/g, '[$1]\n');
// Matches wrapped in [] to help visualise

"[Lorem ipsum dolor sit amet, cons]
[ectetur adipiscing elit, sed do ]
[eiusmod tempor incididunt ut lab]
[ore et dolore magna aliqua.]
"

到目前为止,这仅将字符串切成32个字符。之所以有效,是因为它是自己的换行符,它在每个序列的开始之后标记了序列。

要破坏单词,在贪婪量词{1,32}之后需要一个限定词,以防止它选择以单词中间结尾的序列。断字符char \b可能在换行符开头引起空格,因此必须使用空格char \s代替。还必须将其放置在组外才能食用,以防止将最大宽度增加1个字符:

str.replace(/([^\n]{1,32})\s/g, '[$1]\n');
// Matches wrapped in [] to help visualise

"[Lorem ipsum dolor sit amet,]
[consectetur adipiscing elit, sed]
[do eiusmod tempor incididunt ut]
[labore et dolore magna]
aliqua."

现在它在限制之前的单词上断开,但是由于没有终止空间,最后一个单词和句点在最后一个序列中不匹配。

可以在空白处添加一个“或字符串结尾”选项(\s|$)来扩展匹配,但是最好完全不匹配最后一行 ,因为它会导致在末尾插入不必要的换行符。为此,可以在之前添加完全相同的序列的否定前瞻,但要使用字符串结束字符而不是空格字符:

str.replace(/(?![^\n]{1,32}$)([^\n]{1,32})\s/g, '[$1]\n');
// Matches wrapped in [] to help visualise

"[Lorem ipsum dolor sit amet,]
[consectetur adipiscing elit, sed]
[do eiusmod tempor incididunt ut]
labore et dolore magna aliqua."

答案 1 :(得分:12)

这是一个更短的解决方案:

var str = "This is a very long line of text that we are going to use in this example to divide it into rows of maximum 40 chars."

var result = stringDivider(str, 40, "<br/>\n");
console.log(result);

function stringDivider(str, width, spaceReplacer) {
    if (str.length>width) {
        var p=width
        for (;p>0 && str[p]!=' ';p--) {
        }
        if (p>0) {
            var left = str.substring(0, p);
            var right = str.substring(p+1);
            return left + spaceReplacer + stringDivider(right, width, spaceReplacer);
        }
    }
    return str;
}

此函数使用递归来解决问题。

答案 2 :(得分:3)

我的变种。它保持文字完整,因此它可能并不总是符合maxChars标准。

function wrapText(text, maxChars) {
        var ret = [];
        var words = text.split(/\b/);

        var currentLine = '';
        var lastWhite = '';
        words.forEach(function(d) {
            var prev = currentLine;
            currentLine += lastWhite + d;

            var l = currentLine.length;

            if (l > maxChars) {
                ret.push(prev.trim());
                currentLine = d;
                lastWhite = '';
            } else {
                var m = currentLine.match(/(.*)(\s+)$/);
                lastWhite = (m && m.length === 3 && m[2]) || '';
                currentLine = (m && m.length === 3 && m[1]) || currentLine;
            }
        });

        if (currentLine) {
            ret.push(currentLine.trim());
        }

        return ret.join("\n");
    }

答案 3 :(得分:3)

这样的许多行为可以使用正则表达式(使用具有最少匹配字符数的非贪婪量词,或具有最大字符数的贪心量词,取决于您需要的行为)来实现。

下面,显示了非贪婪的全局替换在Node V8 REPL中工作,因此您可以看到命令和结果。但是同样应该在浏览器中工作。

此模式搜索与定义的组匹配的至少10个字符(\ w表示单词字符,\ s表示空白字符),并将模式固定在\ b字边界上。然后,它使用反向引用将原始匹配替换为附加了换行符的匹配(在这种情况下,可选择替换在括号中的反向引用中未捕获的空格字符)。

> s = "This is a paragraph with several words in it."
'This is a paragraph with several words in it.'
> s.replace(/([\w\s]{10,}?)\s?\b/g, "$1\n")
'This is a \nparagraph \nwith several\nwords in it\n.'

在原始海报的要求格式中,这可能看起来像......

var str = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It w as popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.";

function wordWrap(text,width){
    var re = new RegExp("([\\w\\s]{" + (width - 2) + ",}?\\w)\\s?\\b", "g")
    return text.replace(re,"$1\n")
}

> wordWrap(str,40)
'Lorem Ipsum is simply dummy text of the\nprinting and typesetting industry. Lorem Ipsum has been the industry\'s standard dummy text ever since the 1500s\n, when an unknown printer took a galley of\ntype and scrambled it to make a type specimen\nbook. It has survived not only five centuries\n, but also the leap into electronic typesetting\n, remaining essentially unchanged. It w as popularised in the 1960s with the\nrelease of Letraset sheets containing Lorem\nIpsum passages, and more recently with desktop publishing\nsoftware like Aldus PageMaker including\nversions of Lorem Ipsum.'

答案 4 :(得分:3)

我的版本。 它返回一个行数而不是一个字符串,因为它对你想要使用的行分隔符(如换行符或html BR)更灵活。

wordWrapToStringList(textToWrap, 80).join('<br/>');

将行数组转换为字符串:

helper_method :enrolled_users
  def enrolled_users
    lesson = Lesson.find(params[:id])
    enrollments = lesson.enrollments
    enrolled_users = enrollments.map { |enrollment| enrollment.user }
  end

请注意,它只会自动换行,不会破坏长话,而且可能不是最快的。

答案 5 :(得分:1)

这是基于javabeangrinder解决方案的扩展答案,该解决方案还包含多段输入的文本:

<tr ng-repeat="value in sd.data track by $index | orderBy: syscode">

答案 6 :(得分:1)

在使用正则表达式和其他实现寻找完美的解决方案之后。我决定改正自己。它并不完美,但对我的情况很有用,也许当你的所有文字都是大写字母时,它可能无法正常工作。

&#13;
&#13;
function breakTextNicely(text, limit, breakpoints) {

      var parts = text.split(' ');
      var lines = [];
      text = parts[0];
      parts.shift();

      while (parts.length > 0) {
        var newText = `${text} ${parts[0]}`;

        if (newText.length > limit) {
          lines.push(`${text}\n`);
          breakpoints--;

          if (breakpoints === 0) {
            lines.push(parts.join(' '));
            break;
          } else {
          	text = parts[0];
    	  }
        } else {
          text = newText;
        }
    	  parts.shift();
      }

      if (lines.length === 0) {
        return text;
      } else {
        return lines.join('');
      }
    }

    var mytext = 'this is my long text that you can break into multiple line sizes';
    console.log( breakTextNicely(mytext, 20, 3) );
&#13;
&#13;
&#13;

答案 7 :(得分:0)

function GetWrapedText(text, maxlength) {    
    var resultText = [""];
    var len = text.length;    
    if (maxlength >= len) {
        return text;
    }
    else {
        var totalStrCount = parseInt(len / maxlength);
        if (len % maxlength != 0) {
            totalStrCount++
        }

        for (var i = 0; i < totalStrCount; i++) {
            if (i == totalStrCount - 1) {
                resultText.push(text);
            }
            else {
                var strPiece = text.substring(0, maxlength - 1);
                resultText.push(strPiece);
                resultText.push("<br>");
                text = text.substring(maxlength - 1, text.length);
            }
        }
    }
    return resultText.join("");
}

答案 8 :(得分:0)

厌倦了正则表达式和难以阅读的功能?您可以使用内置的Array方法来包装文本。这是一种方法(只需将100限制替换为您想要的长度:

let string = 'Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry\'s standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.';

string.split(' ').map((value, index, array) => {
    if (!array.currentLineLength) {array.currentLineLength = 0}
    array.currentLineLength += value.length+1;
    if (array.currentLineLength > 100) {
        array.currentLineLength = value.length;
        return "\n" + value;
    }
    return value;
}).join(' ');

也许您还想在每行上缩进此文本?没问题,您只需在最后一个join之后添加即可:

.split("\n").map(value => ''.padEnd(20) + value).join("\n");

答案 9 :(得分:0)

我正在使用一个简单的ajax和javascript实践来做到这一点

var protest = "France is actually the worlds most bad country consisting of people and president full of mentaly gone persons and the people there are causing the disturbance and very much problem in the whole of the world.France be aware that one day there will be no france but you will be highly abused of your bad acts.France go to hell.";

protest = protest.replace(/(.{100})/g, "$1<br>");

document.write(protest);

答案 10 :(得分:0)

const newString = string.split(' ').reduce((acc, curr) => {
  if(acc[acc.length - 1].length > 100) {
    acc[acc.length - 1] = acc[acc.length - 1].concat(" ").concat(curr);
    acc.push(""); // new line
  } else {
    acc[acc.length - 1] = acc[acc.length - 1].concat(" ").concat(curr);
  }
  return acc;
}, [""]).join("\n");

console.log(newString)

答案 11 :(得分:0)

我知道我来晚了,但只是想分享代码以获取任何评论/改进。学习和即兴编写代码总是很有趣的。

import React, {useEffect} from "react";
import { Switch, Route, Redirect } from "react-router-dom";
// creates a beautiful scrollbar
import PerfectScrollbar from "perfect-scrollbar";
import "perfect-scrollbar/css/perfect-scrollbar.css";
// @material-ui/core components
import { makeStyles } from "@material-ui/core/styles";
// core components
import Navbar from "components/Navbars/Navbar.js";
import Footer from "components/Footer/Footer.js";
import Sidebar from "components/Sidebar/Sidebar.js";

import routes from "routes.js";

import styles from "assets/jss/material-dashboard-react/layouts/adminStyle.js";

import lgImage from "assets/img/site-logo.png";
import bgImage from 'assets/img/sidebar-2.jpg';
import { Notifications } from 'react-push-notification';

let ps;

const switchRoutes = (
  <Switch>
    {routes.map((prop, key) => {
      if (prop.layout === "/admin") {
        return (
          <Route
            path={prop.layout + prop.path}
            component={prop.component}
            key={key}
          />
        );
      }
      return null;
    })}
    <Redirect from="/admin" to="/admin/login" />
  </Switch>
);

const useStyles = makeStyles(styles);

export default function Admin({ ...rest }) {
  // styles
  const classes = useStyles();
  // ref to help us initialize PerfectScrollbar on windows devices
  const mainPanel = React.createRef();
  // states and functions
  const [image, setImage] = React.useState(bgImage);
  const [logo, setLogo] = React.useState(lgImage);
  const [color, setColor] = React.useState("blue");
  const [fixedClasses, setFixedClasses] = React.useState("dropdown show");
  const [mobileOpen, setMobileOpen] = React.useState(false);

 

  useEffect(() => {
    setLogo(logo);
  }, [])


  const handleColorClick = color => {
    setColor(color);
  };
  const handleFixedClick = () => {
    if (fixedClasses === "dropdown") {
      setFixedClasses("dropdown show");
    } else {
      setFixedClasses("dropdown");
    }
  };
  const handleDrawerToggle = () => {
    setMobileOpen(!mobileOpen);
  };
  const getRoute = () => {
    return window.location.pathname !== "/admin/maps";
  };

  const handleSideBarLogin = () => {
    return window.location.pathname == "/admin/login"
  }


  const resizeFunction = () => {
    if (window.innerWidth >= 960) {
      setMobileOpen(false);
    }
  };
  // initialize and destroy the PerfectScrollbar plugin
  React.useEffect(() => {
    if (navigator.platform.indexOf("Win") > -1) {
      ps = new PerfectScrollbar(mainPanel.current, {
        suppressScrollX: true,
        suppressScrollY: false
      });
      document.body.style.overflow = "hidden";
    }
    window.addEventListener("resize", resizeFunction);
    // Specify how to clean up after this effect:
    return function cleanup() {
      if (navigator.platform.indexOf("Win") > -1) {
        ps.destroy();
      }
      window.removeEventListener("resize", resizeFunction);
    };
  }, [mainPanel]);

  return (
    <div className={classes.wrapper}>
       <Notifications />
      {handleSideBarLogin() ?
     null :  <Sidebar
     routes={routes}
     logoText={"02DesignStudio"}
     logo={logo}
     image={image}
     handleDrawerToggle={handleDrawerToggle}
     open={mobileOpen}
     color={color}
     {...rest}
    />
      }

      <div className={classes.mainPanel} ref={mainPanel}>
        <Navbar
          routes={routes}
          handleDrawerToggle={handleDrawerToggle}
          {...rest}
        />
        {getRoute() ? (
          <div className={classes.content}>
            <div className={classes.container}>{switchRoutes}</div>
          </div>
        ) : (
          <div className={classes.map}>{switchRoutes}</div>
        )}
        {getRoute() ? <Footer /> : null}

      </div>
    </div>
  );
}