這篇文章帶給大家的內容是關於透過封裝一個v-clamp的指令處理多行文字的溢出,有一定的參考價值,有需要的朋友可以參考一下,希望對你有所幫助。
最近做專案時,遇到了一個需求:要求div里文字在兩行顯示,div的寬度是固定的,如果溢出的話就顯示省略號。單行文字的溢出問題,我們都很熟悉,只要加入以下css屬性就ok:
overflow: hidden; white-space: nowrap; //段落中文本不换行 text-overflow: ellipsis;
查了資料之後發現還是有辦法的
在WebKit瀏覽器或行動端(絕大部分是WebKit內核的瀏覽器)的頁面實作比較簡單,可以直接使用WebKit的CSS擴充屬性(WebKit是私有屬性)-webkit-line-clamp ;注意:這是一個 不規範的屬性(unsupported WebKit property),它沒有出現在 CSS 規範草案中。 -webkit-line-clamp用來限制在一個區塊元素顯示的文字的行數。為了實現該效果,它需要組合其他的WebKit屬性。
我們用程式碼即可實現:
overflow : hidden; text-overflow: ellipsis; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
但是這樣處理會遇到相容性問題,在Firefox瀏覽器上就不起作用。
為了解決相容性問題,有一個clamp.js[https://www.npmjs.com/package...]很好的解決這個問題。
為了更好的跟Vue結合,今天我們就封裝一個v-clamp的指令,來方便的解決這個問題。
// 注册一个全局自定义指令 `v-clamp` Vue.directive('clamp', { // 当被绑定的元素插入到 DOM 中时…… update: function (el, binding) { function clamp(element, options) { options = options || {}; var self = this, win = window, opt = { clamp: options.clamp || 2, useNativeClamp: typeof(options.useNativeClamp) != 'undefined' ? options.useNativeClamp : true, splitOnChars: options.splitOnChars || ['.', '-', '–', '—', ' '], //Split on sentences (periods), hypens, en-dashes, em-dashes, and words (spaces). animate: options.animate || false, truncationChar: options.truncationChar || '…', truncationHTML: options.truncationHTML }, sty = element.style, originalText = element.innerHTML, supportsNativeClamp = typeof(element.style.webkitLineClamp) != 'undefined', clampValue = opt.clamp, isCSSValue = clampValue.indexOf && (clampValue.indexOf('px') > -1 || clampValue.indexOf('em') > -1), truncationHTMLContainer; if (opt.truncationHTML) { truncationHTMLContainer = document.createElement('span'); truncationHTMLContainer.innerHTML = opt.truncationHTML; } // UTILITY FUNCTIONS __________________________________________________________ /** * Return the current style for an element. * @param {HTMLElement} elem The element to compute. * @param {string} prop The style property. * @returns {number} */ function computeStyle(elem, prop) { if (!win.getComputedStyle) { win.getComputedStyle = function(el, pseudo) { this.el = el; this.getPropertyValue = function(prop) { var re = /(\-([a-z]){1})/g; if (prop == 'float') prop = 'styleFloat'; if (re.test(prop)) { prop = prop.replace(re, function() { return arguments[2].toUpperCase(); }); } return el.currentStyle && el.currentStyle[prop] ? el.currentStyle[prop] : null; }; return this; }; } return win.getComputedStyle(elem, null).getPropertyValue(prop); } /** * Returns the maximum number of lines of text that should be rendered based * on the current height of the element and the line-height of the text. */ function getMaxLines(height) { var availHeight = height || element.clientHeight, lineHeight = getLineHeight(element); return Math.max(Math.floor(availHeight / lineHeight), 0); } /** * Returns the maximum height a given element should have based on the line- * height of the text and the given clamp value. */ function getMaxHeight(clmp) { var lineHeight = getLineHeight(element); return lineHeight * clmp; } /** * Returns the line-height of an element as an integer. */ function getLineHeight(elem) { var lh = computeStyle(elem, 'line-height'); if (lh == 'normal') { // Normal line heights vary from browser to browser. The spec recommends // a value between 1.0 and 1.2 of the font size. Using 1.1 to split the diff. lh = parseInt(computeStyle(elem, 'font-size')) * 1.2; } return parseInt(lh); } // MEAT AND POTATOES (MMMM, POTATOES...) ______________________________________ var splitOnChars = opt.splitOnChars.slice(0), splitChar = splitOnChars[0], chunks, lastChunk; /** * Gets an element's last child. That may be another node or a node's contents. */ function getLastChild(elem) { //Current element has children, need to go deeper and get last child as a text node if (elem.lastChild.children && elem.lastChild.children.length > 0) { return getLastChild(Array.prototype.slice.call(elem.children).pop()); } //This is the absolute last child, a text node, but something's wrong with it. Remove it and keep trying else if (!elem.lastChild || !elem.lastChild.nodeValue || elem.lastChild.nodeValue === '' || elem.lastChild.nodeValue == opt.truncationChar) { elem.lastChild.parentNode.removeChild(elem.lastChild); return getLastChild(element); } //This is the last child we want, return it else { return elem.lastChild; } } /** * Removes one character at a time from the text until its width or * height is beneath the passed-in max param. */ function truncate(target, maxHeight) { if (!maxHeight) { return; } /** * Resets global variables. */ function reset() { splitOnChars = opt.splitOnChars.slice(0); splitChar = splitOnChars[0]; chunks = null; lastChunk = null; } var nodeValue = target.nodeValue.replace(opt.truncationChar, ''); //Grab the next chunks if (!chunks) { //If there are more characters to try, grab the next one if (splitOnChars.length > 0) { splitChar = splitOnChars.shift(); } //No characters to chunk by. Go character-by-character else { splitChar = ''; } chunks = nodeValue.split(splitChar); } //If there are chunks left to remove, remove the last one and see if // the nodeValue fits. if (chunks.length > 1) { // console.log('chunks', chunks); lastChunk = chunks.pop(); // console.log('lastChunk', lastChunk); applyEllipsis(target, chunks.join(splitChar)); } //No more chunks can be removed using this character else { chunks = null; } //Insert the custom HTML before the truncation character if (truncationHTMLContainer) { target.nodeValue = target.nodeValue.replace(opt.truncationChar, ''); element.innerHTML = target.nodeValue + ' ' + truncationHTMLContainer.innerHTML + opt.truncationChar; } //Search produced valid chunks if (chunks) { //It fits if (element.clientHeight <= maxHeight) { //There's still more characters to try splitting on, not quite done yet if (splitOnChars.length >= 0 && splitChar !== '') { applyEllipsis(target, chunks.join(splitChar) + splitChar + lastChunk); chunks = null; } //Finished! else { return element.innerHTML; } } } //No valid chunks produced else { //No valid chunks even when splitting by letter, time to move //on to the next node if (splitChar === '') { applyEllipsis(target, ''); target = getLastChild(element); reset(); } } //If you get here it means still too big, let's keep truncating if (opt.animate) { setTimeout(function() { truncate(target, maxHeight); }, opt.animate === true ? 10 : opt.animate); } else { return truncate(target, maxHeight); } } function applyEllipsis(elem, str) { elem.nodeValue = str + opt.truncationChar; } // CONSTRUCTOR ________________________________________________________________ if (clampValue == 'auto') { clampValue = getMaxLines(); } else if (isCSSValue) { clampValue = getMaxLines(parseInt(clampValue)); } var clampedText; if (supportsNativeClamp && opt.useNativeClamp) { sty.overflow = 'hidden'; sty.textOverflow = 'ellipsis'; sty.webkitBoxOrient = 'vertical'; sty.display = '-webkit-box'; sty.webkitLineClamp = clampValue; if (isCSSValue) { sty.height = opt.clamp + 'px'; } } else { var height = getMaxHeight(clampValue); if (height <= element.clientHeight) { console.log(getLastChild(element)); clampedText = truncate(getLastChild(element), height); } } return { 'original': originalText, 'clamped': clampedText }; } clamp(el,{clamp: 2}) } })
其實很簡單,只是把clamp.js中的函數搬過來了。然後就可以這樣來使用:
<div class="txt" v-clamp>很抱歉!没有搜索到相关模板很抱歉!没有搜索到相关模板很抱歉!没有搜索到相关模板很抱歉!没有搜索到相关模板</div>
以上是透過封裝一個v-clamp的指令處理多行文字的溢出的詳細內容。更多資訊請關注PHP中文網其他相關文章!