bootstrap-table
Version:
An extended table to integration with some of the most widely used CSS frameworks. (Supports Bootstrap, Semantic UI, Bulma, Material Design, Foundation)
196 lines (174 loc) • 5.69 kB
JavaScript
import DOMHelper from '../helpers/dom.js'
/**
* DOM manipulation utility functions.
*
* This module provides helper functions for DOM manipulation using native JavaScript,
* including scrollbar width calculation, class name conversion, style parsing,
* h() function for element creation, and HTML-to-DOM conversion.
*
* Note: For a full jQuery-like DOM manipulation library, see src/helpers/dom.js
*
* @module utils/dom
*/
let cachedWidth
/**
* Gets the width of the browser scrollbar.
* The result is cached after the first call for performance.
*
* @returns {number} The width of the scrollbar in pixels.
*/
export function getScrollBarWidth () {
if (cachedWidth === undefined) {
const inner = DOMHelper.create('<div class="fixed-table-scroll-inner"></div>')
const outer = DOMHelper.create('<div class="fixed-table-scroll-outer"></div>')
DOMHelper.append(outer, inner)
DOMHelper.append(document.body, outer)
const w1 = inner.offsetWidth
DOMHelper.css(outer, 'overflow', 'scroll')
let w2 = inner.offsetWidth
if (w1 === w2) {
w2 = outer.clientWidth
}
DOMHelper.remove(outer)
cachedWidth = w1 - w2
}
return cachedWidth
}
/**
* Converts a class specification to a string.
* Handles string, array, and object formats.
*
* @param {string|Array|Object.<string, boolean>} class_ - The class specification.
* @returns {string} The class names as a space-separated string.
*/
export function classToString (class_) {
if (typeof class_ === 'string') {
return class_
}
if (Array.isArray(class_)) {
return class_.map(x => classToString(x)).filter(x => x).join(' ')
}
if (class_ && typeof class_ === 'object') {
return Object.entries(class_).map(([k, v]) => v ? k : '').filter(x => x).join(' ')
}
return ''
}
/**
* Parses and applies CSS styles to a DOM element.
* Supports string, array, and object formats. Handles !important priority.
*
* @param {HTMLElement} dom - The DOM element to apply styles to.
* @param {string|Array|Object.<string, string>} style - The style(s) to apply.
* @returns {HTMLElement} The DOM element with styles applied.
*/
export function parseStyle (dom, style) {
if (!style) {
return dom
}
// Helper function to handle !important priority
const IMPORTANT_PRIORITY_REGEX = /\s*!important\s*$/i
const parsePriority = value => {
if (typeof value === 'string' && IMPORTANT_PRIORITY_REGEX.test(value)) {
return {
value: value.replace(IMPORTANT_PRIORITY_REGEX, ''),
priority: 'important'
}
}
return { value, priority: '' }
}
if (typeof style === 'string') {
style.split(';').forEach(i => {
const index = i.indexOf(':')
if (index > 0) {
const k = i.substring(0, index).trim()
const v = i.substring(index + 1).trim()
const { value, priority } = parsePriority(v)
dom.style.setProperty(k, value, priority)
}
})
} else if (Array.isArray(style)) {
for (const item of style) {
parseStyle(dom, item)
}
} else if (typeof style === 'object') {
for (const [k, v] of Object.entries(style)) {
const { value, priority } = parsePriority(v)
dom.style.setProperty(k, value, priority)
}
}
return dom
}
/**
* Creates a DOM element with attributes and children.
* This function provides a shorthand syntax for creating DOM elements.
*
* @param {string|HTMLElement} element - The tag name or existing element.
* @param {Object.<string, *>} [attrs={}] - The attributes to set on the element.
* @param {Array.<HTMLElement|string>} [children=[]] - The children to append.
* @returns {HTMLElement} The created or modified element.
*/
export function h (element, attrs, children) {
const el = element instanceof HTMLElement ? element : document.createElement(element)
const _attrs = attrs || {}
const _children = children || []
// default attributes
if (el.tagName === 'A') {
el.href = 'javascript:'
}
for (const [k, v] of Object.entries(_attrs)) {
if (v === undefined) {
continue
}
if (['text', 'innerText'].includes(k)) {
el.innerText = v
} else if (['html', 'innerHTML'].includes(k)) {
el.innerHTML = v
} else if (k === 'children') {
_children.push(...v)
} else if (k === 'class') {
el.setAttribute('class', classToString(v))
} else if (k === 'style') {
if (typeof v === 'string') {
el.setAttribute('style', v)
} else {
parseStyle(el, v)
}
} else if (k.startsWith('@') || k.startsWith('on')) {
// event handlers
const event = k.startsWith('@') ? k.substring(1) : k.substring(2).toLowerCase()
const args = Array.isArray(v) ? v : [v]
el.addEventListener(event, ...args)
} else if (k.startsWith('.')) {
// set property
el[k.substring(1)] = v
} else {
el.setAttribute(k, v)
}
}
if (_children.length) {
el.append(..._children)
}
return el
}
/**
* Converts HTML to DOM nodes.
* Uses duck typing to detect jQuery objects without direct dependency.
*
* @param {string|Node|Object} html - The HTML to convert. Can be a string, Node, or jQuery-like object.
* @returns {NodeList|Array<Node>} The DOM nodes.
*/
export function htmlToNodes (html) {
// Duck typing check for jQuery objects (check for 'jquery' property)
if (html && typeof html === 'object' && 'jquery' in html) {
return Array.from(html)
}
if (html instanceof Node) {
return [html]
}
if (typeof html !== 'string') {
html = new String(html).toString()
}
const d = document.createElement('div')
d.innerHTML = html
return d.childNodes
}