lifecycle.js 2.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081
  1. import { startObservingMutations, onAttributesAdded, onElAdded, onElRemoved, cleanupAttributes } from "./mutation"
  2. import { deferHandlingDirectives, directives } from "./directives"
  3. import { dispatch } from './utils/dispatch'
  4. import { nextTick } from "./nextTick"
  5. import { walk } from "./utils/walk"
  6. import { warn } from './utils/warn'
  7. export function start() {
  8. if (! document.body) warn('Unable to initialize. Trying to load Alpine before `<body>` is available. Did you forget to add `defer` in Alpine\'s `<script>` tag?')
  9. dispatch(document, 'alpine:init')
  10. dispatch(document, 'alpine:initializing')
  11. startObservingMutations()
  12. onElAdded(el => initTree(el, walk))
  13. onElRemoved(el => destroyTree(el))
  14. onAttributesAdded((el, attrs) => {
  15. directives(el, attrs).forEach(handle => handle())
  16. })
  17. let outNestedComponents = el => ! Root(el.parentElement, true)
  18. Array.from(document.querySelectorAll(allSelectors()))
  19. .filter(outNestedComponents)
  20. .forEach(el => {
  21. initTree(el)
  22. })
  23. dispatch(document, 'alpine:initialized')
  24. }
  25. let rootSelectorCallbacks = []
  26. let initSelectorCallbacks = []
  27. export function rootSelectors() {
  28. return rootSelectorCallbacks.map(fn => fn())
  29. }
  30. export function allSelectors() {
  31. return rootSelectorCallbacks.concat(initSelectorCallbacks).map(fn => fn())
  32. }
  33. export function addRootSelector(selectorCallback) { rootSelectorCallbacks.push(selectorCallback) }
  34. export function addInitSelector(selectorCallback) { initSelectorCallbacks.push(selectorCallback) }
  35. export function closestRoot(el, includeInitSelectors = false) {
  36. return findClosest(el, element => {
  37. const selectors = includeInitSelectors ? allSelectors() : rootSelectors()
  38. if (selectors.some(selector => element.matches(selector))) return true
  39. })
  40. }
  41. export function findClosest(el, callback) {
  42. if (! el) return
  43. if (callback(el)) return el
  44. if (! el.parentElement) return
  45. return findClosest(el.parentElement, callback)
  46. }
  47. export function isRoot(el) {
  48. return rootSelectors().some(selector => el.matches(selector))
  49. }
  50. export function initTree(el, walker = walk) {
  51. deferHandlingDirectives(() => {
  52. walker(el, (el, skip) => {
  53. directives(el, el.attributes).forEach(handle => handle())
  54. el._x_ignore && skip()
  55. })
  56. })
  57. }
  58. function destroyTree(root) {
  59. walk(root, el => cleanupAttributes(el))
  60. }