问题
I am trying to create a custom element which wraps tinyMCE functionality.
I have the following:-
(function(xtag) {
xtag.register('x-tinymce', {
lifecycle:{
created: tinymceCreate,
removed: tinymceDestroy
},
accessors: {
disabled: {
attribute: {
boolean: true
},
get: getDisabledAttribute,
set: setDisabledAttribute
}
}
});
function tinymceCreate(){
var textarea = document.createElement('textarea');
var currentElement = this;
currentElement.textAreaId = xtag.uid();
textarea.id = currentElement.textAreaId;
currentElement.appendChild(textarea);
currentElement.currentMode = 'design';
var complexConfig = {
selector: '#' + currentElement.textAreaId,
setup: editorSetup
}
tinymce.init(complexConfig)
.then(function thenRetrieveEditor(editors) {
currentElement.currentEditor = editors[0];
currentElement.currentEditor.setMode(currentElement.currentMode ? currentElement.currentMode : 'design');
});
function editorSetup(editor) {
editor.on('blur', function blur(event) {
editor.save();
document.getElementById(editor.id).blur();
xtag.fireEvent(currentElement, 'blur', { detail: event, bubbles: false, cancellable: true });
});
editor.on('focus', function focus(event) {
xtag.fireEvent(currentElement, 'focus', { detail: event, bubbles: false, cancellable: true });
});
editor.on('BeforeSetContent', function beforeSetContent(ed) {
if (ed.content)
ed.content = ed.content.replace(/\t/ig, ' ');
});
}
}
function tinymceDestroy() {
if (this.currentEditor)
tinymce.remove(this.currentEditor);
}
function getDisabledAttribute() {
return this.currentMode === 'readonly';
}
function setDisabledAttribute(value) {
if (value) {
this.currentMode = 'readonly';
}
else {
this.currentMode = 'design';
}
if (this.currentEditor) {
this.currentEditor.setMode(this.currentMode);
}
}
})(xtag);
Now, when I register a blur event, it does get called, but so does the focus event. I think that this is because focus/blur events are captured by x-tag by default. I don't want it to do that. Instead, I want these events fired when the user focusses/blurs tinymce.
I am using xtags 1.5.11 and tinymce 4.4.3.
Update 1
OK, the problem is when I call:-
xtag.fireEvent(currentElement, 'focus', { detail: event, bubbles: false, cancellable: true });
This caused the focus to be lost on the editor and go to the containing eleemnt (x-tinymce). To counter this, I modified my editorSetup to look like this:-
function editorSetup(editor) {
// // Backspace is not detected in keypress, so need to include keyup event as well.
// editor.on('keypress change keyup focus', function(ed) {
// $j("#" + editor.id).trigger(ed.type);
// });
var isFocusFromEditor = false;
var isBlurFromEditor = false;
editor.on('blur', function blurEvent(event) {
console.log("blurred editor");
if (!isFocusFromEditor) {
editor.save();
xtag.fireEvent(currentElement, 'blur', { detail: event, bubbles: false, cancellable: false });
}
else {
console.log('refocussing');
isFocusFromEditor = false;
editor.focus();
isBlurFromEditor = true;
}
});
editor.on('focus', function focusEvent(event) {
console.log("Focus triggered");
isFocusFromEditor = true;
xtag.fireEvent(currentElement, 'focus', { detail: event, bubbles: false, cancellable: false });
});
editor.on('BeforeSetContent', function beforeSetContent(ed) {
if (ed.content) {
ed.content = ed.content.replace(/\t/ig, ' ');
}
});
}
This stops the blur event from triggering, unfortuantely, now, the blur event does not get called when you leave the editing area.
This feels like a tinyMCE problem, just not sure what.
回答1:
Here is a solution that catches focus
and blur
events at the custom element level.
It uses the event.stopImmediatePropagation()
method to stop the transmission of the blur
event to other (external) event listeners when needed.
Also, it uses 2 hidden <input>
(#FI
and #FO
) controls in order to catch the focus when the tab key is pressed.
document.registerElement('x-tinymce', {
prototype: Object.create(HTMLElement.prototype, {
createdCallback: {
value: function() {
var textarea = this.querySelector('textarea')
var output = this.querySelector('output')
output.textContent = "state"
var self = this
self.textAreaId = this.dataset.id // xtag.uid();
textarea.id = self.textAreaId
var complexConfig = {
selector: '#' + self.textAreaId,
setup: editorSetup,
}
var FI = this.querySelector('#FI')
FI.addEventListener('focus', function(ev) {
if (ev.relatedTarget) {
var ev = new FocusEvent('focus')
self.dispatchEvent(ev)
} else {
var ev = new FocusEvent('blur')
self.dispatchEvent(ev)
focusNextElement(-1)
}
})
var FO = this.querySelector('#FO')
FO.addEventListener('focus', function(ev) {
if (!ev.relatedTarget) {
var ev = new FocusEvent('blur')
self.dispatchEvent(ev)
focusNextElement(1)
} else {
var ev = new FocusEvent('focus')
self.dispatchEvent(ev)
}
})
var focused = false
this.addEventListener('focus', function(ev) {
console.log('{focus} in ', this.localName)
if (!focused) {
focused = true
output.textContent = focused
self.editor.focus()
} else {
console.error('should not be here')
ev.stopImmediatePropagation()
}
})
this.addEventListener('blur', function(ev) {
console.log('{blur} in %s', this.localName)
if (focused) {
focused = false
output.textContent = focused
} else {
console.log('=> cancel blur')
ev.stopImmediatePropagation()
}
})
tinymce.init(complexConfig).then(function(editors) {
//self.currentEditor = editors[0]
})
//private
function focusNextElement(diff) {
//add all elements we want to include in our selection
var focussableElements = 'a:not([disabled]), button:not([disabled]), input[type=text]:not([disabled]), [tabindex]:not([disabled]):not([tabindex="-1"])'
if (document.activeElement) {
var focussable = Array.prototype.filter.call(document.querySelectorAll(focussableElements), function(element) {
//check for visibility while always include the current activeElement
return element.offsetWidth > 0 || element.offsetHeight > 0 || element === document.activeElement
})
var index = focussable.indexOf(document.activeElement)
focussable[index + diff].focus()
}
}
function editorSetup(editor) {
console.warn('editor setup')
self.editor = editor
editor.on('focus', function(event) {
if (!focused) {
var ev = new FocusEvent('focus')
self.dispatchEvent(ev)
}
})
editor.on('blur', function(event) {
//if ( focused )
{
var ev = new FocusEvent('blur')
self.dispatchEvent(ev)
}
})
}
}
},
detachedCallback: {
value: function() {
if (this.editor)
tinymce.remove(this.editor)
}
}
})
})
var xElem = document.querySelector('x-tinymce')
xElem.addEventListener('focus', function(ev) {
console.info('focus!')
})
xElem.addEventListener('blur', function(ev) {
console.info('blur!')
})
<script src="http://cdn.tinymce.com/4/tinymce.min.js"></script>
<input type="text">
<x-tinymce data-id="foo">
<output id=output></output>
<input type=text id=FI style='width:0;height:0;border:none'>
<textarea>content</textarea>
<input type=text id=FO style='width:0;height:0;border:none'>
</x-tinymce>
<input type="text">
来源:https://stackoverflow.com/questions/39939211/stop-x-tags-from-capturing-focus-blur-events