for some purpose i want to cache the Window.Event object in a variable and use it later,but MSIE keep telling me that this is 'unknow'.
just run the code below in IE,you will see what i mean
i just want to ask how can this happen?
did i miss something?
html:
<button id='btn'>Click!!!</button>
JS:
var eventObj = null;
document.getElementById('btn').onclick = function() {
eventObj = window.event;
setTimeout(function() {
alert(typeof eventObj.srcElement);
}, 1000)
}
EIDT 1:
i have search some test done by other ,see the below:
HTML :
<button id='btn1'>Click 1 !!</button>
<button id="btn2">Click 2 !!</button>
JS
var btn1EventObj = null;
document.getElementById('btn1').onclick = function() {
btn1EventObj = window.event;
alert(btn1EventObj.srcElement.id);
}
document.getElementById('btn2').onclick = function() {
alert(btn1EventObj === window.event); // output:false;
alert(btn1EventObj.srcElement === window.event.srcElement); // output: true ;
alert(btn1EventObj.srcElement.id); // output: btn2 ;
}
when the btn1 has been click i assume i cache the 'event object' in the btn1EventObj,and then click the btn2:
test:
btn1EventObj === window.event -> false; // there is not only one event object in MSIE
btn1EventObj.srcElement === window.event.srcElement -> true // i can not understand this one the the below.
btn1EventObj.srcElement.id ->btn2
see the fiddle
so all the above tell me that maybe all the event raised in MSIE are all share attributes ,and when the btn2 is clicked,all the previous attribute are overwrite by the later one?
am i kind of right ?
You can't copy window.event out of context of the actual event in IE. In other words: there is no existing event when you assign the handler in the script. If you want to refer to the window.event, the handler has to be assigned inline.
MSDN:
The event object is available only during an event—that is,
you can use it in event handlers but not in other code.
HTML:
<button id='btn' onclick="clicker(event);">Click!!!</button>
and JS:
function clicker(e){
setTimeout(function (){
alert(e.srcElement);
},1000);
return;
}
event in MSDN.
the `window.event' is a global object and may be changed on each event that raised. many events raised on browser within miliseconds.
NOTE:
if you set var y = someObject the object was not copied into y variable, the y variable just contains the address of the object in memory(you can google about: object reference and pointers ).so you need to copy object into y , or try alternative solution below:
so you need to use function(e){} style event handlers:
var eventObj = null;
document.getElementById('btn').onclick = function(e) {
eventObj = e;
setTimeout(function() {
alert(typeof eventObj.srcElement);
}, 1000)
}
see this on jsfiddle
EDIT 1:
in M$ IE you can use this code:
function copyObject(o){
return {srcElement: o.srcElement,
...
more attributes ...
};
}
var eventObj = null;
document.getElementById('btn').onclick = function(e) {
eventObj = copyObject(window.event);
setTimeout(function() {
alert(typeof eventObj.srcElement);
}, 1000)
}
see on jsfiddle(Edited)
var eventObj = null;
document.getElementById('btn').onclick = function() {
eventObj = window.event;
setTimeout((function(eventObj) {
alert(typeof eventObj.srcElement);
})(eventObj), 1000)
}
DEMO.
The event object is available only during an event-that is, you can use it in event handlers but not in other code and you are using setTimeout so it's not available after the event has been executed because to avoid conflict IE set it to null and after 1 second it's not available, so you can use a closure.
Related
I want to assign a JavaScript function to an event that already has a handler function. The new function should not change or remove (unassign) the existing function.
As an example:
I have a function called exFunction() that is already assigned to document.onmousemove. I have another function newFun() to assign to the same event. What I want to happen is when the document.onmousemove event occurs both functions are triggered.
newFun() is not a static function. It is going to be changing according to the webpage. (otherwise I can just write another function that calls both functions).
pure JavaScript only
By using addEventListener, you can apply multiple functions.
document.addEventListener('mousemove', exFunction)
document.addEventListener('mousemove', newFun)
You can also do like this
document.addEventListener("mousemove", function() {
myFunction();
someother();
});
As mentioned in other answers, you can easily assign several eventListeners to the event.
Note that the name of the function is the address of the code to execute and this address is immutable. The snippet shows what works and what doesn't.
var myDiv = document.getElementById('my-div');
function foo1(e) {
console.log('foo1');
if (e.altKey)
foo1 = foo2; //this doesn't work
};
function foo2(e) {
console.log('foo2');
if (e.shiftKey) {
myDiv.removeEventListener('click', foo2);
myDiv.addEventListener('click', foo3);
}
};
function foo3(e) {
console.log('foo3');
if (e.shiftKey) {
myDiv.removeEventListener('click', foo3);
myDiv.addEventListener('click', foo2);
}
};
//assign **addresses** of functions to eventListener
myDiv.addEventListener('click', foo1);
myDiv.addEventListener('click', foo2);
<div id="my-div" style="width:100px;height:100px;border:solid 1px"></div>
addEventListener is the right way to do this however time to time you may need to do this over the DOM element property event listeners by monkey patching.
Forgive me for partially snatching #AlexKudryashev's snippet.
var myDiv = document.getElementById('my-div'),
myBut = document.getElementById('my-button'),
f1 = e => console.log(`clicked "${e.currentTarget.id}" and f1 invoked`);
f2 = e => console.log(`clicked "${e.currentTarget.id}" and f2 invoked`);
monkeyPatch = (f1,f2) => e => (f1(e),f2(e));
myDiv.onclick = f1;
myBut.onclick = function(e){
console.log(`f2 monkey patched to f1. Click My Div to see the effect`);
myDiv.onclick = monkeyPatch(myDiv.onclick, f2);
}
<div id="my-div" style="width:100px;height:100px;border:solid 1px">My Div</div>
<button id="my-button">Monkey Patch f2</button>
I'm using the Microsoft Translation Widget, which I'd like to use to automatically translate a webpage without user interaction.
The problem is, I can't get rid of the widget that keeps popping up or hide it on document.ready because the CSS and JS get loaded from Microsoft's own script in the widget!
Does anyone know a way around this? I've looked everywhere and cannot find a solutuion for this.
Whoa, after some time playing around with that, I've finally achieved what you want.
It's kindda ugly, because of some needed workarounds, but it works, take a look at the fiddle.
The steps were:
Firstly, we must override the default addEventListener behavior:
var addEvent = EventTarget.prototype.addEventListener;
var events = [];
EventTarget.prototype.addEventListener = function(type, listener) {
addEvent.apply(this, [].slice.call(arguments));
events.push({
element: this,
type: type,
listener: listener
});
}
Then, we create a helper function removeEvents. It removes all the event listeners of an element.
var removeEvents = function(el, type) {
var elEvents = events.filter(function(ev) {
return ev.element === el && (type ? ev.type === type : true);
});
for (var i = 0; i < elEvents.length; i++) {
el.removeEventListener(elEvents[i].type, elEvents[i].listener);
}
}
When creating the script tag, in the way Microsoft says:
var s = d.createElement('script');
s.type = 'text/javascript';
s.charset = 'UTF-8';
s.src = ((location && location.href && location.href.indexOf('https') == 0) ? 'https://ssl.microsofttranslator.com' : 'http://www.microsofttranslator.com') + '/ajax/v3/WidgetV3.ashx?siteData=ueOIGRSKkd965FeEGM5JtQ**&ctf=True&ui=true&settings=Manual&from=';
var p = d.getElementsByTagName('head')[0] || d.dElement;
p.insertBefore(s, p.firstChild);
We must add a load event listener to that script, and the code below is fully commented:
s.addEventListener('load', function() {
// when someone changes the translation, the plugin calls the method TranslateArray
// then, we save the original method in a variable, and we override it
var translate = Microsoft.Translator.TranslateArray;
Microsoft.Translator.TranslateArray = function() {
// we call the original method
translate.apply(this, [].slice.call(arguments));
// since the translation is not immediately available
// and we don't have control when it will be
// I've created a helper function to wait for it
waitForTranslation(function() {
// as soon as it is available
// we get all the elements with an attribute lang
[].forEach.call(d.querySelectorAll('[lang]'), function(item, i) {
// and we remove all the mouseover event listeners of them
removeEvents(item, 'mouseover');
});
});
}
// this is the helper function which waits for the translation
function waitForTranslation(cb) {
// since we don't have control over the translation callback
// the workaround was to see if the Translating label is visible
// we keep calling the function, until it's hidden again
// and then we call our callback
var visible = d.getElementById('FloaterProgressBar').style.visibility;
if (visible === 'visible') {
setTimeout(function() {
waitForTranslation(cb);
}, 0);
return;
}
cb();
}
});
Update 1
After re-reading your question, it seems you want to hide all the widgets at all.
So, you must add the following code as soon as the translation is got:
waitForTranslation(function() {
document.getElementById('MicrosoftTranslatorWidget').style.display = 'none';
document.getElementById('WidgetLauncher').style.display = 'none';
document.getElementById('LauncherTranslatePhrase').style.display = 'none';
document.getElementById('TranslateSpan').style.display = 'none';
document.getElementById('LauncherLogo').style.display = 'none';
document.getElementById('WidgetFloaterPanels').style.display = 'none';
// rest of the code
});
I've created another fiddle for you, showing that new behavior.
Update 2
You can prevent the widget showing at all by adding the following CSS code:
#MicrosoftTranslatorWidget, #WidgetLauncher, #LauncherTranslatePhrase, #TranslateSpan, #LauncherLogo, #WidgetFloaterPanels {
opacity: 0!important;
}
And you can even prevent the before-translated text being showed, by hiding the document.body by default, and then showing it when the page is fully translated:
(function(w, d) {
document.body.style.display = 'none';
/* (...) */
s.addEventListener('load', function() {
var translate = Microsoft.Translator.TranslateArray;
Microsoft.Translator.TranslateArray = function() {
translate.apply(this, [].slice.call(arguments));
waitForTranslation(function() {
/* (...) */
document.body.style.display = 'block';
});
}
});
});
Take a look at the final fiddle I've created.
For me, this was the solution:
on your < style > section add this class
.LTRStyle { display: none !important }
Also, if you are invoking the translation widget this way:
Microsoft.Translator.Widget.Translate('en', lang, null, null, TranslationDone, null, 3000);
then add this to your callback (in this example is TranslationDone) function:
function TranslationDone() {
Microsoft.Translator.Widget.domTranslator.showHighlight = false;
Microsoft.Translator.Widget.domTranslator.showTooltips = false;
document.getElementById('WidgetFloaterPanels').style.display = 'none';
};
I have been writing a plugin, and i really like this format
Function.prototype._onClick = function() {
// do something
}
Fuction.prototype.addListner = function() {
this.$element.on('click', this._onClick.bind(this));
}
the problem is sometimes i need the element being clicked and the main object. Doing as below i loose the dom element and not using bind looses the main object.
Fuction.prototype.addListner {
this.$element.find('.some-class').on('click', this._onClick.bind(this));
}
To achieve that i go back to ugly version
Fuction.prototype.addListner = function() {
var self = this;
this.$element.find('.some-class').on('click', function() {
self._onClick($(this));
});
}
Is there any better way to do this?
As zerkms, you can use the event.target to achieve what you want.
When using .on, the handler is :
handler
Type: Function( Event eventObject [, Anything extraParameter ] [, ...
] ) A function to execute when the event is triggered. The value false
is also allowed as a shorthand for a function that simply does return
false.
So your _onClick function will receive click event as its 1st parameter, then from event.target, you can now get the clicked item.
var Test = function(sel) {
this.$element = $(sel);
this.value = 'My value is ' + this.$element.data('val');
};
Test.prototype.addListner = function() {
this.$element.find('.some-class').on('click', this._onClick.bind(this));
}
Test.prototype._onClick = function(evt) {
// Get the target which is being clicked.
var $taget = $(evt.target);
//
console.log(this.value);
// use $target to get the clicke item.
console.log($taget.data('val'));
}
var test = new Test('#test');
test.addListner();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
<div id="test" data-val="divVal">
<button class="some-class" data-val="Button-A">btnA</button>
<button class="some-class" data-val="Button-B">btnB</button>
</div>
I'm creating a custom WinJS control with an event listener. For simplicity, this example should fire an event whenever it is tapped.
This is created with the markup:
<div class="alphaNavBar" data-win-control="MobMan.Controls.AlphaNavBar"></div>
The control is implemented here. It throws an "invalid argument" exception at the dispatchEvent(...) line.
(function () {
var alphaNavBar = WinJS.Class.define(function (el, options) {
// Create control
var self = this;
this._element = el || document.createElement("div");
this._element.winControl = this;
this._element.innerText = "Hello World!";
this._selection = false;
// Listen for tap
this._element.addEventListener("MSPointerDown", function (evt) {
// Toggle selection
self._selection = !self._selection;
// Selection changed, fire event
// Invalid argument here
self._element.dispatchEvent("mySelectionChanged", { selection: self._selection });
// Invalid argument here
});
});
// Add to global namespace
WinJS.Namespace.define("MobMan.Controls", {
AlphaNavBar: alphaNavBar
});
// Mixin event properties
WinJS.Class.mix(MobMan.Controls.AlphaNavBar, WinJS.Utilities.createEventProperties("mySelectionChanged"), WinJS.UI.DOMEventMixin);
})();
This event is listened to by:
var alphaNavBar = document.querySelector(".alphaNavBar");
alphaNavBar.addEventListener("mySelectionChanged", function (evt) {
// Should fire when alphaNavBar is tapped
debugger;
});
What am I doing wrong here?
I posted my question here as well and got an answer modifying the event dispatch like so:
// Listen for tap
this._element.addEventListener("MSPointerDown", function (evt) {
// Toggle selection
this._selection = !this._selection;
// Create the event.
var _event = document.createEvent('customevent');
// Define that the event name is 'mySelectionChanged' and pass details.
_event.initCustomEvent('mySelectionChanged', true, true, { selection: this._selection });
// Selection changed, fire event
this.dispatchEvent(_event);
});
This was able to trigger the event correctly for me. Still not sure what I was doing wrong before, but it is fixed now.
I would like to convert the event handler to a jquery style click event but it doesnt seem to like passing the event through, perhaps its because its not an anonymous function anymore?
// variables
var faqOne = document.getElementById("faqOne");
var $hiddenOne = $(".faqOneHidden");
// javascript event handler works!
faqOne.addEventListener("click", function(e){
showFaqOne.showClickedFaq(e);
}, false);
// javascript event handle - doesnt work!
$("#faqOne").click(function(){
showFaqOne.showClickedFaq(e);
});
// constructor
function DisplayQFaqs(link, faq){
this.link = link;
this.faq = faq;
}
// method prototype
DisplayQFaqs.prototype.showClickedFaq = function(e){
var el = e.currentTarget;
if(el === this.link) {
this.faq.toggle("slow", function(){
});
}
};
// new DisplayQFaqs Objects
var showFaqOne = new DisplayQFaqs(faqOne,$hiddenOne);
Your e is undefined inside
$("#faqOne").click(function(){
showFaqOne.showClickedFaq(e);
});
Change it to
$("#faqOne").click(function(e){//Now e is there
showFaqOne.showClickedFaq(e);
});