Created
February 23, 2023 05:20
-
-
Save wangziling/1dcff3275dcebb377f6bd0d27f6aec0a to your computer and use it in GitHub Desktop.
Vue2 v-html directive. Supports HTML contents.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { DirectiveOptions } from 'vue'; | |
import _ from 'lodash'; | |
import $ from 'jquery'; | |
const _toString = Object.prototype.toString; | |
/** | |
* Copied from vue2 source code. | |
* @param val {*} | |
* @return {string} | |
*/ | |
function vToString (val: any) { | |
return val == null | |
? '' | |
: Array.isArray(val) || (_.isPlainObject(val) && val.toString === _toString) | |
? JSON.stringify(val, null, 2) | |
: String(val); | |
} | |
/** | |
* Update the inner HTML content. | |
* @param el {Element} | |
* @param binding {*} | |
*/ | |
const updateInnerHTMLContent: DirectiveOptions['inserted'] = function (el, binding) { | |
if (!el) { | |
return; | |
} | |
const isElMountedOnOwnerDoc = $.contains(el.ownerDocument.documentElement, el); | |
const $el = $(el); | |
// Refer to `v-html`. | |
const bindingValue = vToString(binding.value); | |
// Remove old children, make sure that old children will not be remained unexpectedly. | |
$el | |
.empty() | |
.append(bindingValue); | |
// If the element hadn't been mounted yet. | |
// The JQuery will not execute the inner scripts. | |
if (!isElMountedOnOwnerDoc) { | |
// We create a fake style element and prepend to the el. | |
const fakeStyleEle = document.createElement('style'); | |
fakeStyleEle.innerHTML = '.__fake_style__ { color: inherit; }'; | |
function refreshDOMContent () { | |
// OK. do it again. | |
$el | |
.empty() | |
.append(bindingValue); | |
} | |
// If IE. browser. | |
if (typeof el.ownerDocument.contains !== 'function') { | |
/** @see https://developer.mozilla.org/en-US/docs/Web/API/MutationEvent **/ | |
// Deprecate event. But useful for IE. | |
el.addEventListener('DOMNodeInserted', function DOMNodeInserted (e: Event) { | |
// If inserting the fakeStyleEl. | |
if (e.target === fakeStyleEle) { | |
// Refresh. | |
refreshDOMContent(); | |
// Remove listener. | |
el.removeEventListener('DOMNodeInserted', DOMNodeInserted); | |
} | |
}); | |
} else { | |
// When it is 'onload', ok, means the whole el is mounted on the doc. | |
fakeStyleEle.onload = function elOnload () { | |
// Refresh. | |
refreshDOMContent(); | |
// Remove listener. | |
fakeStyleEle.onload = null; | |
}; | |
} | |
/** @see https://developer.mozilla.org/en-US/docs/Web/API/Element/insertAdjacentElement **/ | |
// Prepend to observe the el mounted event. | |
// $el.prepend(fakeStyleEle); | |
el.insertAdjacentElement('afterbegin', fakeStyleEle); | |
} | |
}; | |
// Export. | |
export const htmlDirectiveOptions: DirectiveOptions = { | |
inserted: function (el, binding) { | |
// @ts-ignore | |
updateInnerHTMLContent(...arguments); | |
}, | |
// This hook will be triggered when component and sub-component updated. | |
componentUpdated: function (el, binding) { | |
// If value changed. | |
if ( | |
// Forcibly. | |
_.get(binding, 'modifiers.forceUpdate') || | |
binding.value !== binding.oldValue | |
) { | |
// @ts-ignore | |
updateInnerHTMLContent(...arguments); | |
} | |
} | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Usage:
This directive supports excutable
<script />
elements and will execute them by following their order.E.g.
The
someContents
is:You will see the
Hello World
alert after the two remote js files all loaded or failed to fetch.