富文本編輯器-4-工具欄Tooltip模塊

Quill默認(rèn)工具欄沒有提示信息宦赠,在playground有Tooltip的實(shí)現(xiàn)例子胸嘴,是基于BootStrap的Tooltip實(shí)現(xiàn)的券犁,但我們也可以用較少代碼埠对,手寫一個(gè)Tooltip模塊络断。

CSS實(shí)現(xiàn)

工具欄上的圖標(biāo)樣式改為相對(duì)定位,再給每個(gè)圖標(biāo)加上屬性tooltip鸠窗,[tooltip]相對(duì)圖標(biāo)定位妓羊,再用偽元素編寫小箭頭和內(nèi)容,就可以實(shí)現(xiàn)提示信息的效果稍计。

index.js

import Quill from 'quill'
import {tooltips} from './tooltip-list.js'

class Tooltip {
  constructor (quill) {
    this.quill = quill
    this.toolbar = quill.getModule('toolbar')
    this.buttons = null
    this.selectors = null
    let toolbarElement = this.toolbar.container
    if (toolbarElement) {
      this.buttons = toolbarElement.querySelectorAll('button')
      this.selectors = toolbarElement.querySelectorAll('.ql-picker')
      for (let el of this.buttons) {
        this.setTooltip(el)
      }
      for (let el of this.selectors) {
        this.setTooltip(el)
      }
    }
  }

  setTooltip (el) {
    let format = [].find.call(el.classList, (className) => {
      return className.indexOf('ql-') === 0
    }).replace('ql-', '')
    let tip = ''
    if (tooltips[format]) {
      let tool = tooltips[format]
      if (typeof tool === 'string') {
        tip = tool
      } else {
        let value = el.value || ''
        if (value != null && tool[value]) {
          tip = tool[value]
        }
      }
    }
    Object.assign(el.style, {
      'position': 'relative'
    })
    el.setAttribute('tooltip', tip)
  }

}

Quill.register('modules/tooltip', Tooltip)

tooltip.css (參考pure-css-tooltips)

.ql-toolbar [tooltip]{
  position:relative;
}
.ql-toolbar [tooltip]::before{
  content: "";
  position: absolute;
  top:-4px;
  left:50%;
  transform: translateX(-50%);
  border-width: 4px 6px 0 6px;
  border-style: solid;
  border-color: rgba(0,0,0,0.7) transparent transparent transparent;
  z-index: 99;
  opacity:0;
}

.ql-toolbar [tooltip-position='left']::before{
  left:0%;
  top:50%;
  margin-left:-12px;
  transform:translatey(-50%) rotate(-90deg)
}
.ql-toolbar [tooltip-position='top']::before{
  left:50%;
}
.ql-toolbar [tooltip-position='bottom']::before{
  top:100%;
  margin-top:8px;
  transform: translateX(-50%) translatey(-100%) rotate(-180deg)
}
.ql-toolbar [tooltip-position='right']::before{
  left:100%;
  top:50%;
  margin-left:1px;
  transform:translatey(-50%) rotate(90deg)
}

.ql-toolbar [tooltip]::after {
  content: attr(tooltip);
  position: absolute;
  left:50%;
  top:-4px;
  transform: translateX(-50%) translateY(-100%);
  background: rgba(0,0,0,0.7);
  text-align: center;
  color: #fff;
  padding:4px 2px;
  font-size: 12px;
  min-width: 70px;
  border-radius: 5px;
  pointer-events: none;
  padding: 4px 4px;
  z-index:99;
  opacity:0;
}

.ql-toolbar [tooltip-position='left']::after{
  left:0%;
  top:50%;
  margin-left:-8px;
  transform: translateX(-100%) translateY(-50%);
}
.ql-toolbar [tooltip-position='top']::after{
  left:50%;
}
.ql-toolbar [tooltip-position='bottom']::after{
  top:100%;
  margin-top:8px;
  transform: translateX(-50%) translateY(0%);
}
.ql-toolbar [tooltip-position='right']::after{
  left:100%;
  top:50%;
  margin-left:8px;
  transform: translateX(0%) translateY(-50%);
}

.ql-toolbar [tooltip]:hover::after,.ql-toolbar [tooltip]:hover::before {
   opacity:1
}

.quill-tooltip::before{
  content: "";
  position: absolute;
  bottom:-4px;
  left:50%;
  transform: translateX(-50%);
  border-width: 4px 6px 0 6px;
  border-style: solid;
  border-color: rgba(0,0,0,0.7) transparent transparent transparent;
  z-index: 99;
}

JS實(shí)現(xiàn)

JS實(shí)現(xiàn)較為麻煩躁绸,需要?jiǎng)?chuàng)建tooltip元素,append到body上臣嚣,監(jiān)聽mouseenter和mouseleave净刮,顯示或隱藏tooltip。但因?yàn)槭窍鄬?duì)body定位硅则,這樣的tooltip避免了被頁面上其他元素遮擋的問題淹父。
另外,滾動(dòng)時(shí)會(huì)出現(xiàn)tooltip跟隨鼠標(biāo)滾動(dòng)的情況怎虫,這時(shí)需要獲取工具欄所在的可滾動(dòng)父元素暑认。在其滾動(dòng)時(shí)困介,隱藏tooltip。

import Quill from 'quill'
import {tooltips} from './tooltip-list.js'
import {getScrollParent} from 'utils/scrollInfo.js'

const tooltipStyles = {
  minWidth: '70px',
  position: 'absolute',
  padding: '4px 8px',
  textAlign: 'center',
  backgroundColor: 'rgba(0,0,0,0.7)',
  color: '#fff',
  cursor: 'default',
  borderRadius: '4px',
  fontSize: '12px',
  top: '-9999px',
  visibility: 'hidden'
}

class Tooltip {
  constructor (quill) {
    this.quill = quill
    this.toolbar = quill.getModule('toolbar')
    this.buttons = null
    this.selectors = null
    this.tip = null
    this.timeout = null
    this.mouseenterHandler = null
    this.mouseleaveHandler = null
    let toolbarElement = this.toolbar.container
    if (toolbarElement) {
      // 添加處理事件
      this.buttons = toolbarElement.querySelectorAll('button')
      this.selectors = toolbarElement.querySelectorAll('.ql-picker')
      for (let el of this.buttons) {
        this.addHandler(el)
      }
      for (let el of this.selectors) {
        this.addHandler(el)
      }
      // 創(chuàng)建tooltip
      this.createTooltip()
      // 滾動(dòng)元素增加handler
      this.scrollElm = getScrollParent(toolbarElement)
      this.scrollElm.addEventListener('scroll', this.mouseleaveHandler)
    }
  }

  createTooltip () {
    this.tip = document.createElement('div')
    this.tip.classList.add('quill-tooltip')
    Object.assign(this.tip.style, tooltipStyles)
    document.body.appendChild(this.tip)
  }

  addHandler (el) {
    this.mouseenterHandler = () => {
      this.timeout = setTimeout(() => {
        this.showTooltip(el)
      }, 100)
    }
    this.mouseleaveHandler = () => {
      if (this.timeout) clearTimeout(this.timeout)
      this.hideTooltip()
    }
    el.addEventListener('mouseenter', this.mouseenterHandler)
    el.addEventListener('mouseleave', this.mouseleaveHandler)
  }

  showTooltip (el) {
    // let format = el.className.replace('ql-', '')
    let format = [].find.call(el.classList, (className) => {
      return className.indexOf('ql-') === 0
    }).replace('ql-', '')
    if (tooltips[format]) {
      let tool = tooltips[format]
      if (typeof tool === 'string') {
        this.tip.textContent = tool
      } else {
        let value = el.value || ''
        if (value != null && tool[value]) {
          this.tip.textContent = tool[value]
        }
      }
      const elRect = el.getBoundingClientRect()
      const tipRect = this.tip.getBoundingClientRect()
      const body = document.documentElement || document.body
      const bodyRect = {
        width: body.scrollWidth,
        height: body.scrollHeight,
        scrollTop: body.scrollTop,
        scrollLeft: body.scrollLeft
      }
      const offset = 3
      Object.assign(this.tip.style, {
        'top': elRect.top - elRect.height - offset + bodyRect.scrollTop + 'px',
        'left': elRect.left - (tipRect.width - elRect.width) / 2 + bodyRect.scrollLeft + 'px',
        'visibility': 'visible'
      })
    }
  }

  hideTooltip () {
    Object.assign(this.tip.style, {
      top: '-9999px',
      visibility: 'hidden'
    })
  }

  onDestroy () {
    console.warn('ondestroy')
    this.destroyTooltip()
    if (this.buttons) {
      for (let el of this.buttons) {
        this.removeHandler(el)
      }
    }
    if (this.selectors) {
      for (let el of this.selectors) {
        this.removeHandler(el)
      }
    }
    if (this.scrollElm) this.scrollElm.removeEventListener('scroll', this.mouseleaveHandler)
  }

  destroyTooltip () {
    if (this.tip.parentNode) this.tip.parentNode.removeChild(this.tip)
  }

  removeHandler (el) {
    el.removeEventListener('mouseenter', this.mouseenterHandler)
    el.removeEventListener('mouseleave', this.mouseleaveHandler)
  }
}

Quill.register('modules/tooltip', Tooltip)

獲取滾動(dòng)父元素的代碼如下(來自ElementUI):

export const getScrollParent = function (element) {
  var parent = element.parentNode
  if (!parent) {
    return element
  }
  if (parent === document) {
    if (document.body.scrollTop !== undefined) {
      return document.body
    } else {
      return document.documentElement
    }
  }
  if (
    ['scroll', 'auto'].indexOf(getStyleComputedProperty(parent, 'overflow')) !== -1 ||
    ['scroll', 'auto'].indexOf(getStyleComputedProperty(parent, 'overflow-x')) !== -1 ||
    ['scroll', 'auto'].indexOf(getStyleComputedProperty(parent, 'overflow-y')) !== -1
  ) {
    return parent
  }
  return getScrollParent(element.parentNode)
}
export const getStyleComputedProperty = function (element, property) {
  var css = window.getComputedStyle(element, null)
  return css[property]
}

Tooltip中的文本

文本通過獲取圖標(biāo)的className來確定蘸际,如果是純字符串座哩,則直接用tooltips對(duì)象查找tooltips[format],如果還有一層粮彤,則根據(jù)其value進(jìn)一步查找根穷。

   let format = [].find.call(el.classList, (className) => {
      return className.indexOf('ql-') === 0
    }).replace('ql-', '')
    if (tooltips[format]) {
      let tool = tooltips[format]
      if (typeof tool === 'string') {
        this.tip.textContent = tool
      } else {
        let value = el.value || ''
        if (value != null && tool[value]) {
          this.tip.textContent = tool[value]
        }
      }

tooltips如下:

export const tooltips = {
  'align': {
    '': '對(duì)齊',
    'center': '居中對(duì)齊',
    'right': '右對(duì)齊',
    'justify': '兩端對(duì)齊'
  },
  'background': '背景色',
  'blockquote': '引用',
  'bold': '加粗',
  'clean': '清除格式',
  'code': '代碼',
  'code-block': '代碼段',
  'color': '顏色',
  'direction': '方向',
  'float': {
    'center': '居中',
    'full': '全浮動(dòng)',
    'left': '左浮動(dòng)',
    'right': '右浮動(dòng)'
  },
  'formula': '公式',
  'header': {
    '': '標(biāo)題',
    '1': '標(biāo)題1',
    '2': '標(biāo)題2'
  },
  'italic': '斜體',
  'image': '圖片',
  'indent': {
    '+1': '縮進(jìn)',
    '-1': '取消縮進(jìn)'
  },
  'link': '鏈接',
  'list': {
    'ordered': '有序列表',
    'bullet': '無序列表',
    'check': '選擇列表'
  },
  'script': {
    'sub': '下標(biāo)',
    'super': '上標(biāo)'
  },
  'strike': '刪除線',
  'underline': '下劃線',
  'video': '視頻',
  'undo': '撤銷',
  'redo': '重做',
  'size': '字號(hào)',
  'font': '字體',
  'divider': '分割線',
  'formatBrush': '格式刷',
  'emoji': '表情'
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市导坟,隨后出現(xiàn)的幾起案子屿良,更是在濱河造成了極大的恐慌,老刑警劉巖惫周,帶你破解...
    沈念sama閱讀 217,509評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件尘惧,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡闯两,警方通過查閱死者的電腦和手機(jī)褥伴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來漾狼,“玉大人,你說我怎么就攤上這事饥臂⊙吩辏” “怎么了?”我有些...
    開封第一講書人閱讀 163,875評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵隅熙,是天一觀的道長稽煤。 經(jīng)常有香客問我,道長囚戚,這世上最難降的妖魔是什么酵熙? 我笑而不...
    開封第一講書人閱讀 58,441評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮驰坊,結(jié)果婚禮上匾二,老公的妹妹穿的比我還像新娘。我一直安慰自己拳芙,他們只是感情好察藐,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著舟扎,像睡著了一般分飞。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上睹限,一...
    開封第一講書人閱讀 51,365評(píng)論 1 302
  • 那天譬猫,我揣著相機(jī)與錄音讯檐,去河邊找鬼。 笑死染服,一個(gè)胖子當(dāng)著我的面吹牛别洪,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播肌索,決...
    沈念sama閱讀 40,190評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蕉拢,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了诚亚?” 一聲冷哼從身側(cè)響起晕换,我...
    開封第一講書人閱讀 39,062評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎站宗,沒想到半個(gè)月后闸准,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,500評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡梢灭,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評(píng)論 3 335
  • 正文 我和宋清朗相戀三年夷家,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片敏释。...
    茶點(diǎn)故事閱讀 39,834評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡库快,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出钥顽,到底是詐尸還是另有隱情义屏,我是刑警寧澤,帶...
    沈念sama閱讀 35,559評(píng)論 5 345
  • 正文 年R本政府宣布蜂大,位于F島的核電站闽铐,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏奶浦。R本人自食惡果不足惜兄墅,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望澳叉。 院中可真熱鬧隙咸,春花似錦、人聲如沸耳高。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽泌枪。三九已至概荷,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間碌燕,已是汗流浹背误证。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評(píng)論 1 269
  • 我被黑心中介騙來泰國打工继薛, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人愈捅。 一個(gè)月前我還...
    沈念sama閱讀 47,958評(píng)論 2 370
  • 正文 我出身青樓遏考,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蓝谨。 傳聞我的和親對(duì)象是個(gè)殘疾皇子灌具,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 問答題47 /72 常見瀏覽器兼容性問題與解決方案? 參考答案 (1)瀏覽器兼容問題一:不同瀏覽器的標(biāo)簽?zāi)J(rèn)的外補(bǔ)...
    _Yfling閱讀 13,751評(píng)論 1 92
  • 1譬巫、通過CocoaPods安裝項(xiàng)目名稱項(xiàng)目信息 AFNetworking網(wǎng)絡(luò)請(qǐng)求組件 FMDB本地?cái)?shù)據(jù)庫組件 SD...
    陽明先生_X自主閱讀 15,980評(píng)論 3 119
  • 心酸芦昔,你有沒有诱贿。那種莫名的心酸,突然間看見朋友圈里一條人家新裝修房子咕缎,新買的衣服珠十,好吃的,都是你一直想要的凭豪。你就開...
    生活不易你要勇敢閱讀 202評(píng)論 0 0
  • 恰逢清明,三兩哥們號(hào)召出去游玩末早,這真是瞌睡遇到枕頭,巧了…由于各方面原因近幾年一直尋不到合適契機(jī)出去散心…...
    萌萌的謫仙兒閱讀 251評(píng)論 0 0
  • 這個(gè)世界上總有那么20%的人说庭,見到你就是莫名其妙的喜歡你然磷,總有那么20%的人,見到你就是莫名其妙的討厭你刊驴,剩余...
    墨尓兮閱讀 157評(píng)論 0 0