Skip to content
GitLab
    • Explore Projects Groups Snippets
Projects Groups Snippets
  • /
  • Help
    • Help
    • Support
    • Community forum
    • Submit feedback
    • Contribute to GitLab
  • Sign in / Register
  • B bootstrap
  • Project information
    • Project information
    • Activity
    • Labels
    • Members
  • Repository
    • Repository
    • Files
    • Commits
    • Branches
    • Tags
    • Contributors
    • Graph
    • Compare
  • Issues 263
    • Issues 263
    • List
    • Boards
    • Service Desk
    • Milestones
  • Merge requests 114
    • Merge requests 114
  • CI/CD
    • CI/CD
    • Pipelines
    • Jobs
    • Schedules
  • Deployments
    • Deployments
    • Environments
    • Releases
  • Packages and registries
    • Packages and registries
    • Package Registry
    • Infrastructure Registry
  • Monitor
    • Monitor
    • Incidents
  • Analytics
    • Analytics
    • Value stream
    • CI/CD
    • Repository
  • Wiki
    • Wiki
  • Snippets
    • Snippets
  • Activity
  • Graph
  • Create a new issue
  • Jobs
  • Commits
  • Issue Boards
Collapse sidebar
  • Bootstrap
  • bootstrap
  • Merge requests
  • !23596

Removing jQuery from Alert

  • Review changes

  • Download
  • Email patches
  • Plain diff
Merged Administrator requested to merge v4-alert-without-jquery into v4-without-jquery 7 years ago
  • Overview 0
  • Commits 7
  • Pipelines 0
  • Changes 19

Created by: Johann-S

I'm near the end for this plugin

The last thing I have to fix is :

  $(document).on(
    Event.CLICK_DATA_API,
    Selector.DISMISS,
    Alert._handleDismiss(new Alert())
  )

And how folks will user our plugins without jQuery, maybe we should enable to register our plugins to jQuery even if we do not use it anymore :thinking:

Work in progress... @XhmikosR if you want to help me do not hesitate :smile:

Compare
  • v4-without-jquery (base)

and
  • latest version
    f862b8eb
    7 commits, 2 years ago

19 files
+ 397
- 120

    Preferences

    File browser
    Compare changes
j‎s‎
s‎rc‎
d‎om‎
dat‎a.js‎ +51 -0
even‎t.js‎ +0 -61
eventHa‎ndler.js‎ +216 -0
selector‎Engine.js‎ +66 -0
aler‎t.js‎ +28 -32
uti‎l.js‎ +3 -3
te‎sts‎
un‎it‎
aler‎t.js‎ +17 -13
vis‎ual‎
alert‎.html‎ +3 -1
butto‎n.html‎ +1 -1
carous‎el.html‎ +1 -1
collap‎se.html‎ +1 -1
dropdo‎wn.html‎ +1 -1
modal‎.html‎ +1 -1
popove‎r.html‎ +1 -1
scrolls‎py.html‎ +1 -1
tab.‎html‎ +1 -1
.eslint‎rc.json‎ +1 -0
index‎.html‎ +3 -1
packag‎e.json‎ +1 -1
js/src/dom/data.js 0 → 100644
+ 51
- 0
  • View file @ f862b8eb

  • Edit in single-file editor

  • Open in Web IDE

/**
* --------------------------------------------------------------------------
* Bootstrap (v4.0.0-beta): dom/data.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* --------------------------------------------------------------------------
*/
const mapData = (() => {
const storeData = {}
return {
set(element, key, data) {
let id
if (element.key === undefined) {
element.key = {
key,
id
}
}
storeData[id] = data
},
get(element, key) {
if (element.key === undefined || element.key !== key) {
return null
}
const keyProperties = element.key
return storeData[keyProperties.id]
},
delete(element, key) {
if (element.key === undefined || element.key !== key) {
return
}
const keyProperties = element.key
delete storeData[keyProperties.id]
}
}
})()
const Data = {
setData(instance, key, data) {
mapData.set(instance, key, data)
},
getData(instance, key) {
mapData.get(instance, key)
},
removeData(instance, key) {
mapData.delete(instance, key)
}
}
export default Data
js/src/dom/event.js deleted 100644 → 0
+ 0
- 61
  • View file @ e5a01386

/**
* --------------------------------------------------------------------------
* Bootstrap (v4.0.0-beta): dom/event.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* --------------------------------------------------------------------------
*/
const TransitionEndEvent = {
WebkitTransition : 'webkitTransitionEnd',
MozTransition : 'transitionend',
OTransition : 'oTransitionEnd otransitionend',
transition : 'transitionend'
}
const Event = {
on(element, event, handler) {
if (typeof event !== 'string') {
return
}
element.addEventListener(event, handler, false)
},
one(element, event, handler) {
const complete = () => {
/* eslint func-style: off */
handler()
element.removeEventListener(event, complete, false)
}
Event.on(element, event, complete)
},
trigger(element, event) {
if (typeof event !== 'string') {
return
}
const eventToDispatch = new CustomEvent(event, {
bubbles: true,
cancelable: true
})
element.dispatchEvent(eventToDispatch)
},
getBrowserTransitionEnd() {
if (window.QUnit) {
return false
}
const el = document.createElement('bootstrap')
for (const name in TransitionEndEvent) {
if (el.style[name] !== undefined) {
return {
end: TransitionEndEvent[name]
}
}
}
return false
}
}
export default Event
js/src/dom/eventHandler.js 0 → 100644
+ 216
- 0
  • View file @ f862b8eb

  • Edit in single-file editor

  • Open in Web IDE

/**
* --------------------------------------------------------------------------
* Bootstrap (v4.0.0-beta): dom/eventHandler.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* --------------------------------------------------------------------------
*/
const TransitionEndEvent = {
WebkitTransition : 'webkitTransitionEnd',
MozTransition : 'transitionend',
OTransition : 'oTransitionEnd otransitionend',
transition : 'transitionend'
}
// defaultPrevented is broken in IE.
// https://connect.microsoft.com/IE/feedback/details/790389/event-defaultprevented-returns-false-after-preventdefault-was-called
const workingDefaultPrevented = (() => {
const e = document.createEvent('CustomEvent')
e.initEvent('Bootstrap', true, true)
e.preventDefault()
return e.defaultPrevented
})()
// CustomEvent polyfill for IE (see: https://mzl.la/2v76Zvn)
if (typeof window.CustomEvent !== 'function') {
window.CustomEvent = (event, params) => {
params = params || {
bubbles: false,
cancelable: false,
detail: undefined
}
const evt = document.createEvent('CustomEvent')
evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail)
if (!workingDefaultPrevented) {
const origPreventDefault = Event.prototype.preventDefault
evt.preventDefault = () => {
if (!evt.cancelable) {
return
}
origPreventDefault.call(evt)
Object.defineProperty(evt, 'defaultPrevented', {
get() {
return true
},
configurable: true
})
}
}
return evt
}
window.CustomEvent.prototype = window.Event.prototype
}
// Event constructor shim
if (!window.Event || typeof window.Event !== 'function') {
const origEvent = window.Event
window.Event = (inType, params) => {
params = params || {}
const e = document.createEvent('Event')
e.initEvent(inType, Boolean(params.bubbles), Boolean(params.cancelable))
return e
}
window.Event.prototype = origEvent.prototype
}
const namespaceRegex = /[^.]*(?=\..*)\.|.*/
const stripNameRegex = /\..*/
// Events storage
const eventRegistry = {}
let uidEvent = 1
function getUidEvent(element, uid) {
return element.uidEvent = uid && `${uid}::${uidEvent++}` || element.uidEvent || uidEvent++
}
function getEvent(element) {
const uid = getUidEvent(element)
return eventRegistry[uid] = eventRegistry[uid] || {}
}
const nativeEvents =
`click,dblclick,mouseup,mousedown,contextmenu,
'mousewheel,DOMMouseScroll,
'mouseover,mouseout,mousemove,selectstart,selectend,
'keydown,keypress,keyup,
'orientationchange,
'touchstart,touchmove,touchend,touchcancel,
'gesturestart,gesturechange,gestureend,
'focus,blur,change,reset,select,submit,
'load,unload,beforeunload,resize,move,DOMContentLoaded,readystatechange,
'error,abort,scroll`.split(',')
function bootstrapHandler(element, fn) {
return function (event) {
return fn.apply(element, [event])
}
}
function bootstrapDelegationHandler(selector, fn) {
return function (event) {
const domElements = document.querySelectorAll(selector)
for (let target = event.target; target && target !== this; target = target.parentNode) {
for (let i = domElements.length; i--;) {
if (domElements[i] === target) {
return fn.apply(target, [event])
}
}
}
// To please ESLint
return null
}
}
const EventHandler = {
on(element, originalTypeEvent, handler, delegationFn) {
if (typeof originalTypeEvent !== 'string'
|| (typeof element === 'undefined' || element === null)) {
return
}
const delegation = typeof handler === 'string'
// allow to get the native events from namespaced events ('click.bs.button' --> 'click')
let typeEvent = originalTypeEvent.replace(stripNameRegex, '')
const isNative = nativeEvents.indexOf(typeEvent) > -1
if (!isNative) {
typeEvent = originalTypeEvent
}
const events = getEvent(element)
const handlers = events[typeEvent] || (events[typeEvent] = {})
const uid = getUidEvent(handler, originalTypeEvent.replace(namespaceRegex, ''))
if (handlers[uid]) {
return
}
const fn = !delegation ? bootstrapHandler(element, handler) : bootstrapDelegationHandler(handler, delegationFn)
handlers[uid] = fn
handler.uidEvent = uid
element.addEventListener(typeEvent, fn, false)
},
one(element, event, handler) {
function complete(e) {
const typeEvent = event.replace(stripNameRegex, '')
const events = getEvent(element)
if (!events || !events[typeEvent]) {
return
}
handler.apply(element, [e])
EventHandler.off(element, event, complete)
}
EventHandler.on(element, event, complete)
},
off(element, originalTypeEvent, handler) {
if (typeof originalTypeEvent !== 'string'
|| (typeof element === 'undefined' || element === null)) {
return
}
const typeEvent = originalTypeEvent.replace(stripNameRegex, '')
const events = getEvent(element)
if (!events || !events[typeEvent]) {
return
}
const uidEvent = handler.uidEvent
const fn = events[typeEvent][uidEvent]
element.removeEventListener(typeEvent, fn, false)
delete events[typeEvent][uidEvent]
},
trigger(element, event) {
if (typeof event !== 'string'
|| (typeof element === 'undefined' || element === null)) {
return null
}
const typeEvent = event.replace(stripNameRegex, '')
const isNative = nativeEvents.indexOf(typeEvent) > -1
let returnedEvent = null
if (isNative) {
const evt = document.createEvent('HTMLEvents')
evt.initEvent(typeEvent, true, true)
element.dispatchEvent(evt)
returnedEvent = evt
} else {
const eventToDispatch = new CustomEvent(event, {
bubbles: true,
cancelable: true
})
element.dispatchEvent(eventToDispatch)
returnedEvent = eventToDispatch
}
return returnedEvent
},
getBrowserTransitionEnd() {
if (window.QUnit) {
return false
}
const el = document.createElement('bootstrap')
for (const name in TransitionEndEvent) {
if (el.style[name] !== undefined) {
return {
end: TransitionEndEvent[name]
}
}
}
return false
}
}
export default EventHandler
js/src/dom/selectorEngine.js 0 → 100644
+ 66
- 0
  • View file @ f862b8eb

  • Edit in single-file editor

  • Open in Web IDE

/**
* --------------------------------------------------------------------------
* Bootstrap (v4.0.0-beta): dom/selectorEngine.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* --------------------------------------------------------------------------
*/
// matches polyfill (see: https://mzl.la/2ikXneG)
let fnMatches = null
if (!Element.prototype.matches) {
fnMatches =
Element.prototype.msMatchesSelector ||
Element.prototype.webkitMatchesSelector
} else {
fnMatches = Element.prototype.matches
}
// closest polyfill (see: https://mzl.la/2vXggaI)
let fnClosest = null
if (!Element.prototype.closest) {
fnClosest = (element, selector) => {
let ancestor = element
if (!document.documentElement.contains(element)) {
return null
}
do {
if (fnMatches.call(ancestor, selector)) {
return ancestor
}
ancestor = ancestor.parentElement
} while (ancestor !== null)
return null
}
} else {
fnClosest = (element, selector) => {
return element.closest(selector)
}
}
const SelectorEngine = {
matches(element, selector) {
return fnMatches.call(element, selector)
},
find(selector) {
if (typeof selector !== 'string') {
return null
}
let selectorType = 'querySelectorAll'
if (selector.indexOf('#') === 0) {
selectorType = 'getElementById'
selector = selector.substr(1, selector.length)
}
return document[selectorType](selector)
},
closest(element, selector) {
return fnClosest(element, selector)
}
}
export default SelectorEngine
js/src/alert.js
+ 28
- 32
  • View file @ f862b8eb

  • Edit in single-file editor

  • Open in Web IDE


import Data from './dom/data'
import EventHandler from './dom/eventHandler'
import SelectorEngine from './dom/selectorEngine'
import Util from './util'
@@ -8,7 +11,7 @@ import Util from './util'
* --------------------------------------------------------------------------
*/
const Alert = (($) => {
const Alert = (() => {
/**
@@ -70,7 +73,7 @@ const Alert = (($) => {
const rootElement = this._getRootElement(element)
const customEvent = this._triggerCloseEvent(rootElement)
if (customEvent.isDefaultPrevented()) {
if (customEvent.defaultPrevented) {
return
}
@@ -78,7 +81,7 @@ const Alert = (($) => {
}
dispose() {
$.removeData(this._element, DATA_KEY)
Data.removeData(this._element, DATA_KEY)
this._element = null
}
@@ -90,42 +93,37 @@ const Alert = (($) => {
let parent = false
if (selector) {
parent = $(selector)[0]
parent = SelectorEngine.find(selector)[0]
}
if (!parent) {
parent = $(element).closest(`.${ClassName.ALERT}`)[0]
parent = SelectorEngine.closest(element, `.${ClassName.ALERT}`)
}
return parent
}
_triggerCloseEvent(element) {
const closeEvent = $.Event(Event.CLOSE)
$(element).trigger(closeEvent)
return closeEvent
return EventHandler.trigger(element, Event.CLOSE)
}
_removeElement(element) {
$(element).removeClass(ClassName.SHOW)
element.classList.remove(ClassName.SHOW)
if (!Util.supportsTransitionEnd() ||
!$(element).hasClass(ClassName.FADE)) {
!element.classList.contains(ClassName.FADE)) {
this._destroyElement(element)
return
}
$(element)
.one(Util.TRANSITION_END, (event) => this._destroyElement(element, event))
EventHandler
.one(element, Util.TRANSITION_END, (event) => this._destroyElement(element, event))
Util.emulateTransitionEnd(element, TRANSITION_DURATION)
}
_destroyElement(element) {
$(element)
.detach()
.trigger(Event.CLOSED)
.remove()
EventHandler.trigger(element, Event.CLOSED)
element.parentNode.removeChild(element)
}
@@ -133,12 +131,11 @@ const Alert = (($) => {
static _jQueryInterface(config) {
return this.each(function () {
const $element = $(this)
let data = $element.data(DATA_KEY)
let data = Data.getData(this, DATA_KEY)
if (!data) {
data = new Alert(this)
$element.data(DATA_KEY, data)
Data.setData(this, DATA_KEY, data)
}
if (config === 'close') {
@@ -165,29 +162,28 @@ const Alert = (($) => {
* Data Api implementation
* ------------------------------------------------------------------------
*/
$(document).on(
Event.CLICK_DATA_API,
Selector.DISMISS,
Alert._handleDismiss(new Alert())
)
EventHandler.on(document, Event.CLICK_DATA_API, Selector.DISMISS, Alert._handleDismiss(new Alert()))
/**
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
* add .alert to jQuery only if jQuery is present
*/
$.fn[NAME] = Alert._jQueryInterface
$.fn[NAME].Constructor = Alert
$.fn[NAME].noConflict = function () {
$.fn[NAME] = JQUERY_NO_CONFLICT
return Alert._jQueryInterface
if (typeof window.$ !== 'undefined' || typeof window.jQuery !== 'undefined') {
const $ = window.$ || window.jQuery
$.fn[NAME] = Alert._jQueryInterface
$.fn[NAME].Constructor = Alert
$.fn[NAME].noConflict = function () {
$.fn[NAME] = JQUERY_NO_CONFLICT
return Alert._jQueryInterface
}
}
return Alert
})(jQuery)
})()
export default Alert
js/src/util.js
+ 3
- 3
  • View file @ f862b8eb

  • Edit in single-file editor

  • Open in Web IDE


import Event from './dom/event'
import EventHandler from './dom/eventHandler'
/**
* --------------------------------------------------------------------------
@@ -9,7 +9,7 @@ import Event from './dom/event'
const Util = (() => {
const transition = Event.getBrowserTransitionEnd()
const transition = EventHandler.getBrowserTransitionEnd()
const MAX_UID = 1000000
@@ -60,7 +60,7 @@ const Util = (() => {
},
triggerTransitionEnd(element) {
Event.trigger(element, Util.TRANSITION_END)
EventHandler.trigger(element, Util.TRANSITION_END)
},
supportsTransitionEnd() {
js/tests/unit/alert.js
+ 17
- 13
  • View file @ f862b8eb

  • Edit in single-file editor

  • Open in Web IDE


@@ -41,8 +41,8 @@ $(function () {
var $alert = $(alertHTML).bootstrapAlert().appendTo($('#qunit-fixture'))
$alert.find('.close').trigger('click')
var closeBtn = $alert.find('.close')[0]
EventHandler.trigger(closeBtn, 'click')
assert.strictEqual($alert.hasClass('show'), false, 'remove .show class on .close click')
})
@@ -56,7 +56,8 @@ $(function () {
assert.notEqual($('#qunit-fixture').find('.alert').length, 0, 'element added to dom')
$alert.find('.close').trigger('click')
var closeBtn = $alert.find('.close')[0]
EventHandler.trigger(closeBtn, 'click')
assert.strictEqual($('#qunit-fixture').find('.alert').length, 0, 'element removed from dom')
})
@@ -64,16 +65,19 @@ $(function () {
QUnit.test('should not fire closed when close is prevented', function (assert) {
assert.expect(1)
var done = assert.async()
$('<div class="alert"/>')
.on('close.bs.alert', function (e) {
e.preventDefault()
assert.ok(true, 'close event fired')
done()
})
.on('closed.bs.alert', function () {
assert.ok(false, 'closed event fired')
})
.bootstrapAlert('close')
var $alert = $('<div class="alert"/>')
$alert.appendTo('#qunit-fixture')
EventHandler.on($alert[0], 'close.bs.alert', function (e) {
e.preventDefault()
assert.ok(true, 'close event fired')
done()
})
EventHandler.on($alert[0], 'closed.bs.alert', function () {
assert.ok(false, 'closed event fired')
})
$alert.bootstrapAlert('close')
})
})
js/tests/visual/alert.html
+ 3
- 1
  • View file @ f862b8eb

  • Edit in single-file editor

  • Open in Web IDE


@@ -45,7 +45,9 @@
</div>
<script src="../../../assets/js/vendor/jquery-slim.min.js"></script>
<script src="../../dist/dom/event.js"></script>
<script src="../../dist/dom/eventHandler.js"></script>
<script src="../../dist/dom/selectorEngine.js"></script>
<script src="../../dist/dom/data.js"></script>
<script src="../../dist/util.js"></script>
<script src="../../dist/alert.js"></script>
</body>
js/tests/visual/button.html
+ 1
- 1
  • View file @ f862b8eb

  • Edit in single-file editor

  • Open in Web IDE


@@ -45,7 +45,7 @@
</div>
<script src="../../../assets/js/vendor/jquery-slim.min.js"></script>
<script src="../../dist/dom/event.js"></script>
<script src="../../dist/dom/eventHandler.js"></script>
<script src="../../dist/util.js"></script>
<script src="../../dist/button.js"></script>
</body>
js/tests/visual/carousel.html
+ 1
- 1
  • View file @ f862b8eb

  • Edit in single-file editor

  • Open in Web IDE


@@ -41,7 +41,7 @@
</div>
<script src="../../../assets/js/vendor/jquery-slim.min.js"></script>
<script src="../../dist/dom/event.js"></script>
<script src="../../dist/dom/eventHandler.js"></script>
<script src="../../dist/util.js"></script>
<script src="../../dist/carousel.js"></script>
js/tests/visual/collapse.html
+ 1
- 1
  • View file @ f862b8eb

  • Edit in single-file editor

  • Open in Web IDE


@@ -58,7 +58,7 @@
</div>
<script src="../../../docs/assets/js/vendor/jquery-slim.min.js"></script>
<script src="../../dist/dom/event.js"></script>
<script src="../../dist/dom/eventHandler.js"></script>
<script src="../../dist/util.js"></script>
<script src="../../dist/collapse.js"></script>
</body>
js/tests/visual/dropdown.html
+ 1
- 1
  • View file @ f862b8eb

  • Edit in single-file editor

  • Open in Web IDE


@@ -119,7 +119,7 @@
<script src="../../../assets/js/vendor/jquery-slim.min.js"></script>
<script src="../../../assets/js/vendor/popper.min.js"></script>
<script src="../../dist/dom/event.js"></script>
<script src="../../dist/dom/eventHandler.js"></script>
<script src="../../dist/util.js"></script>
<script src="../../dist/dropdown.js"></script>
<script src="../../dist/collapse.js"></script>
0 Assignees
None
Assign to
0 Reviewers
None
Request review from
Labels
2
js v4
2
js v4
    Assign labels
  • Manage project labels

Milestone
No milestone
None
None
Time tracking
No estimate or time spent
Lock merge request
Unlocked
2
2 participants
Administrator
XhmikosR
Reference: twbs/bootstrap!23596
Source branch: v4-alert-without-jquery

Menu

Explore Projects Groups Snippets