Skip to content

Commit

Permalink
Add IntersectionObserver to Tinymce initialization
Browse files Browse the repository at this point in the history
the IntersectionObserver will initialize each instance of the Tinymce only if it becomes visible. This can drastically reduce Javascript long tasks on pages with a lot of Tinymce instances.
  • Loading branch information
sascha-karnatz committed May 24, 2023
1 parent 62c6fa2 commit 7cbfc67
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 45 deletions.
6 changes: 3 additions & 3 deletions package/dist/admin.js

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions package/dist/admin.js.map

Large diffs are not rendered by default.

107 changes: 68 additions & 39 deletions package/src/tinymce.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,46 +2,76 @@
//

let tinymceCustomConfigs = {}
let tinymceIntersectionObserver = null

// Returns default config for a tinymce editor.
function getDefaultConfig(id) {
function getDefaultConfig(editorId) {
const config = Alchemy.TinymceDefaults
config.language = Alchemy.locale
config.selector = `#tinymce_${id}`
config.selector = `#${editorId}`
config.init_instance_callback = initInstanceCallback
return config
}

// Returns configuration for given custom tinymce editor selector.
//
// It uses the +.getDefaultConfig+ and merges the custom parts.
//
function getConfig(id, selector) {
const editorConfig = tinymceCustomConfigs[selector] || {}
return {...getDefaultConfig(id), ...editorConfig};
return { ...getDefaultConfig(id), ...editorConfig }
}

// Initializes one specific TinyMCE editor
//
// @param id [Number]
// - Editor id that should be initialized.
//
function initEditor(id) {
const editorId = `tinymce_${id}`
const textarea = document.getElementById(editorId)
const editor = tinymce.get(editorId)
// create intersection observer and register textareas to be initialized when
// they are visible
function initEditors(ids) {
initializeIntersectionObserver()

ids.forEach((id) => {
const editorId = `tinymce_${id}`
const textarea = document.getElementById(editorId)

if (textarea === null) {
console.warn(`Could not initialize TinyMCE for textarea#tinymce_${id}!`)
return
if (textarea) {
tinymceIntersectionObserver.observe(textarea)
} else {
console.warn(`Could not initialize TinyMCE for textarea#${editorId}!`)
}
})
}

// initialize IntersectionObserver if it is not already initialized
// the observer will initialize Tinymce if the textarea becomes visible
function initializeIntersectionObserver() {
if (tinymceIntersectionObserver === null) {
const observerCallback = (entries, observer) => {
entries.forEach((entry) => {
if (entry.intersectionRatio > 0) {
initTinymceEditor(entry.target)
// disable observer after the Tinymce was initialized
observer.unobserve(entry.target)
}
})
}
const options = {
root: Alchemy.ElementEditors.element_area.get(0),
rootMargin: "0px",
threshold: [0.05]
}

tinymceIntersectionObserver = new IntersectionObserver(
observerCallback,
options
)
}
}

// Initializes one specific TinyMCE editor
function initTinymceEditor(textarea) {
const editorId = textarea.id
const config = getConfig(editorId, textarea.classList[1])

// remove editor instance, if already initialized
if (editor) {
editor.remove()
}
removeEditor(editorId)

const config = getConfig(id, textarea.classList[1])
if (config) {
const spinner = new Alchemy.Spinner("small")
textarea.closest(".tinymce_container").prepend(spinner.spin().el.get(0))
Expand All @@ -52,7 +82,6 @@ function initEditor(id) {
}

// Gets called after an editor instance gets initialized
//
function initInstanceCallback(editor) {
const element = document.getElementById(editor.id).closest(".element-editor")
element.getElementsByClassName("spinner").item(0).remove()
Expand All @@ -65,47 +94,47 @@ function initInstanceCallback(editor) {
})
}

export default {
function removeEditor(editorId) {
const editorElement = document.getElementById(editorId)
if (tinymceIntersectionObserver && editorElement) {
tinymceIntersectionObserver.unobserve(editorElement)
}

const editor = tinymce.get(editorId)
if (editor) {
editor.remove()
}
}

export default {
// Initializes all TinyMCE editors with given ids
//
// @param ids [Array]
// - Editor ids that should be initialized.
//
init(ids) {
ids.forEach((id) => initEditor(id))
initEditors(ids)
},

// Initializes TinyMCE editor with given options
//
initWith(options) {
tinymce.init({...Alchemy.TinymceDefaults, ...options})
tinymce.init({ ...Alchemy.TinymceDefaults, ...options })
},

// Removes the TinyMCE editor from given dom ids.
//
remove(ids) {
ids.forEach((id) => {
const editor = tinymce.get(`tinymce_${id}`)
if (editor) {
editor.remove()
}
})
ids.forEach((id) => removeEditor(`tinymce_${id}`))
},

// Remove all tinymce instances for given selector
removeFrom(selector) {
// the selector is a jQuery selector - it has to be refactor if we taking care of the calling methods
$(selector).each(function () {
const elem = tinymce.get(this.id)
if (elem) {
elem.remove()
}
$(selector).each(function (element) {
removeEditor(element.id)
})
},

// set tinymce configuration for a given selector key
setCustomConfig(key, configuration) {
tinymceCustomConfigs[key] = configuration;
tinymceCustomConfigs[key] = configuration
}
}

0 comments on commit 7cbfc67

Please sign in to comment.