diff --git a/js/src/alert.js b/js/src/alert.js
index ab36a8d36383a5e12eca883f469f4f32a2844411..87209a86088e6391938f10bc9005fef2021f522c 100644
--- a/js/src/alert.js
+++ b/js/src/alert.js
@@ -5,7 +5,9 @@
  * --------------------------------------------------------------------------
  */
 
-import $ from 'jquery'
+import Data from './dom/data'
+import EventHandler from './dom/eventHandler'
+import SelectorEngine from './dom/selectorEngine'
 import Util from './util'
 
 /**
@@ -64,7 +66,7 @@ class Alert {
 
     const customEvent = this._triggerCloseEvent(rootElement)
 
-    if (customEvent.isDefaultPrevented()) {
+    if (customEvent.defaultPrevented) {
       return
     }
 
@@ -72,7 +74,7 @@ class Alert {
   }
 
   dispose() {
-    $.removeData(this._element, DATA_KEY)
+    Data.removeData(this._element, DATA_KEY)
     this._element = null
   }
 
@@ -87,52 +89,45 @@ class Alert {
     }
 
     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 (!$(element).hasClass(ClassName.FADE)) {
+    if (!element.classList.contains(ClassName.FADE)) {
       this._destroyElement(element)
       return
     }
 
     const transitionDuration = Util.getTransitionDurationFromElement(element)
 
-    $(element)
-      .one(Util.TRANSITION_END, (event) => this._destroyElement(element, event))
-
-    Util.emulateTransitionEnd(transitionDuration)
+    EventHandler
+      .one(element, Util.TRANSITION_END, (event) => this._destroyElement(element, event))
+    Util.emulateTransitionEnd(element, transitionDuration)
   }
 
   _destroyElement(element) {
-    $(element)
-      .detach()
-      .trigger(Event.CLOSED)
-      .remove()
+    EventHandler.trigger(element, Event.CLOSED)
+    element.parentNode.removeChild(element)
   }
 
   // Static
 
   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') {
diff --git a/js/src/dom/data.js b/js/src/dom/data.js
new file mode 100644
index 0000000000000000000000000000000000000000..bbe807aac1e10770406531996813cf36d69a8cfc
--- /dev/null
+++ b/js/src/dom/data.js
@@ -0,0 +1,51 @@
+/**
+ * --------------------------------------------------------------------------
+ * 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
diff --git a/js/src/dom/event.js b/js/src/dom/eventHandler.js
similarity index 68%
rename from js/src/dom/event.js
rename to js/src/dom/eventHandler.js
index 5968e62e0ff760564ee26878c16fadc5be995d39..0627ccd26864cc41edf06ec11ca22de59a6e327e 100644
--- a/js/src/dom/event.js
+++ b/js/src/dom/eventHandler.js
@@ -1,13 +1,13 @@
 /**
  * --------------------------------------------------------------------------
- * Bootstrap (v4.0.0-beta): dom/event.js
+ * Bootstrap (v4.0.0-beta): dom/eventHandler.js
  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
  * --------------------------------------------------------------------------
  */
 
-const Event = {
+const EventHandler = {
   on(element, event, handler) {
-    if (typeof event !== 'string') {
+    if (typeof event !== 'string' || typeof element === 'undefined') {
       return
     }
     element.addEventListener(event, handler, false)
@@ -19,12 +19,12 @@ const Event = {
       handler()
       element.removeEventListener(event, complete, false)
     }
-    Event.on(element, event, complete)
+    EventHandler.on(element, event, complete)
   },
 
   trigger(element, event) {
-    if (typeof event !== 'string') {
-      return
+    if (typeof event !== 'string' || typeof element === 'undefined') {
+      return null
     }
 
     const eventToDispatch = new CustomEvent(event, {
@@ -32,7 +32,9 @@ const Event = {
       cancelable: true
     })
     element.dispatchEvent(eventToDispatch)
+
+    return eventToDispatch
   }
 }
 
-export default Event
+export default EventHandler
diff --git a/js/src/dom/selectorEngine.js b/js/src/dom/selectorEngine.js
new file mode 100644
index 0000000000000000000000000000000000000000..f6bcf6da265bd9aa81397112b602ee6b6ff31805
--- /dev/null
+++ b/js/src/dom/selectorEngine.js
@@ -0,0 +1,42 @@
+/**
+ * --------------------------------------------------------------------------
+ * Bootstrap (v4.0.0-beta): dom/selectorEngine.js
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
+ * --------------------------------------------------------------------------
+ */
+
+const SelectorEngine = {
+  matches: Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector,
+
+  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) {
+    let ancestor = element
+    if (!document.documentElement.contains(element)) {
+      return null
+    }
+
+    do {
+      if (SelectorEngine.matches.call(ancestor, selector)) {
+        return ancestor
+      }
+
+      ancestor = ancestor.parentElement
+    } while (ancestor !== null)
+
+    return null
+  }
+}
+
+export default SelectorEngine
diff --git a/js/src/util.js b/js/src/util.js
index 2ffdec999bd4518d0a0bda5b9c96b4717f83b3af..b5efeca14e0afcc3ee6ea60fb8b4fa6a58e8b31f 100644
--- a/js/src/util.js
+++ b/js/src/util.js
@@ -5,7 +5,7 @@
  * --------------------------------------------------------------------------
  */
 
-import Event from './dom/event'
+import EventHandler from './dom/eventHandler'
 
 /**
  * ------------------------------------------------------------------------
@@ -78,7 +78,7 @@ const Util = {
   },
 
   triggerTransitionEnd(element) {
-    Event.trigger(element, Util.TRANSITION_END)
+    EventHandler.trigger(element, Util.TRANSITION_END)
   },
 
   // TODO: Remove in v5
diff --git a/js/tests/index.html b/js/tests/index.html
index a3fd937c2dbbdc0fd37eac8b55a25c527dc3b967..db0743f0ffe069dc572bbb0a7c82830c74125672 100644
--- a/js/tests/index.html
+++ b/js/tests/index.html
@@ -97,7 +97,9 @@
     </script>
 
     <!-- Transpiled Plugins -->
-    <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>
     <script src="../dist/button.js"></script>
diff --git a/js/tests/unit/.eslintrc.json b/js/tests/unit/.eslintrc.json
index 86d851ad06d0aa8bc7c37f92aef102a01bae95b9..b07efd269d17026d767beb10cc6c6d2777796d7b 100644
--- a/js/tests/unit/.eslintrc.json
+++ b/js/tests/unit/.eslintrc.json
@@ -12,7 +12,8 @@
     "Button": false,
     "Carousel": false,
     "Simulator": false,
-    "Toast": false
+    "Toast": false,
+    "EventHandler": false
   },
   "parserOptions": {
     "ecmaVersion": 5,
diff --git a/js/tests/unit/alert.js b/js/tests/unit/alert.js
index 65a8f9e76754385d004e1cf781d85ead91fa48ef..32fcc6ceaf7235f9dbba10ba3af59d79e54613d8 100644
--- a/js/tests/unit/alert.js
+++ b/js/tests/unit/alert.js
@@ -70,16 +70,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')
   })
 
   QUnit.test('close should use internal _element if no element provided', function (assert) {
diff --git a/js/tests/visual/alert.html b/js/tests/visual/alert.html
index d2befb8b8abe7d3dfb7c1ebf5babdf16d9fbe29c..9111e357275a25675fa54b2df83ed61b8c196724 100644
--- a/js/tests/visual/alert.html
+++ b/js/tests/visual/alert.html
@@ -52,7 +52,9 @@
     </div>
 
     <script src="../../../node_modules/jquery/dist/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>
diff --git a/js/tests/visual/button.html b/js/tests/visual/button.html
index ce900e471e80d589245f49fcf937f8d2ef5f7b3d..818160da3b69cb45e943d9a524c5d11b47e50e5d 100644
--- a/js/tests/visual/button.html
+++ b/js/tests/visual/button.html
@@ -45,7 +45,7 @@
     </div>
 
     <script src="../../../node_modules/jquery/dist/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>
diff --git a/js/tests/visual/carousel.html b/js/tests/visual/carousel.html
index 895a6422c4b7824db803de1f4a58c0811959d342..d1376b39b9f5c996f4733911d3687bf7c1762108 100644
--- a/js/tests/visual/carousel.html
+++ b/js/tests/visual/carousel.html
@@ -46,7 +46,7 @@
     </div>
 
     <script src="../../../node_modules/jquery/dist/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>
     <script>
diff --git a/js/tests/visual/collapse.html b/js/tests/visual/collapse.html
index 2bc3c086ee5f703d9d63551ceb3d3af580111f8a..e084bd08bc721cf1e53f88c1482b58916a63cb9f 100644
--- a/js/tests/visual/collapse.html
+++ b/js/tests/visual/collapse.html
@@ -72,7 +72,7 @@
     </div>
 
     <script src="../../../node_modules/jquery/dist/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>
diff --git a/js/tests/visual/dropdown.html b/js/tests/visual/dropdown.html
index 9e4a10cfb6eca9623018b4162efe267327d9f71c..d1bb0ed35080c63bb5f375ffb56034cf2e82e67d 100644
--- a/js/tests/visual/dropdown.html
+++ b/js/tests/visual/dropdown.html
@@ -205,7 +205,7 @@
 
     <script src="../../../node_modules/jquery/dist/jquery.slim.min.js"></script>
     <script src="../../../node_modules/popper.js/dist/umd/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>
diff --git a/js/tests/visual/modal.html b/js/tests/visual/modal.html
index 53ad81a97ac9ecb9bd39cb58b29c34c74aaf1f98..30975b0c7bb539b8efce82fb4d0f42b3facf52f6 100644
--- a/js/tests/visual/modal.html
+++ b/js/tests/visual/modal.html
@@ -207,13 +207,12 @@
 
     <script src="../../../node_modules/jquery/dist/jquery.slim.min.js"></script>
     <script src="../../../node_modules/popper.js/dist/umd/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/modal.js"></script>
     <script src="../../dist/collapse.js"></script>
     <script src="../../dist/tooltip.js"></script>
     <script src="../../dist/popover.js"></script>
-
     <script>
       var firefoxTestDone = false
       function reportFirefoxTestResult(result) {
diff --git a/js/tests/visual/popover.html b/js/tests/visual/popover.html
index 38ab65a1256a7294bb29a5128498dead6ec3ac62..e422891dadff81510de26ddda46804bcf343187d 100644
--- a/js/tests/visual/popover.html
+++ b/js/tests/visual/popover.html
@@ -33,11 +33,10 @@
 
     <script src="../../../node_modules/jquery/dist/jquery.slim.min.js"></script>
     <script src="../../../node_modules/popper.js/dist/umd/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/tooltip.js"></script>
     <script src="../../dist/popover.js"></script>
-
     <script>
       $(function () {
         $('[data-toggle="popover"]').popover()
diff --git a/js/tests/visual/scrollspy.html b/js/tests/visual/scrollspy.html
index 2fb4c1ac2bb14e14b771e174b03f87dfac0f2028..f0149198d727dcb60e3631902e87328055d905bc 100644
--- a/js/tests/visual/scrollspy.html
+++ b/js/tests/visual/scrollspy.html
@@ -88,7 +88,7 @@
 
     <script src="../../../node_modules/jquery/dist/jquery.slim.min.js"></script>
     <script src="../../../site/docs/4.2/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/scrollspy.js"></script>
     <script src="../../dist/dropdown.js"></script>
diff --git a/js/tests/visual/tab.html b/js/tests/visual/tab.html
index 737679a7801876031980dbf5100c1317696fd3c6..3b8ce4026f49b9f983cd905720966657d1ad8be8 100644
--- a/js/tests/visual/tab.html
+++ b/js/tests/visual/tab.html
@@ -227,7 +227,7 @@
 
     <script src="../../../node_modules/jquery/dist/jquery.slim.min.js"></script>
     <script src="../../../node_modules/popper.js/dist/umd/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/tab.js"></script>
     <script src="../../dist/dropdown.js"></script>