JavaScript Function callback and events - javascript

I was trying to make a javascript gallery script by my own.
When i have done with it i was pretty happy, until i noticed, that it doesn't work in IE6.
In FireFox everything looks fine. So i started debugging.
I noticed, that setAttribute is one of the problems for sure. Maybe even the biggest.
So after viewing an interetsing article about setting onclick property with parameters i was kind of happy, but one thing stayed unsolved for me. Using callback method is tricky, but i just don't know how to pass event object that way. Here is the old code sample:
var miniatury = document.getElementsByTagName ("a");
function zoom (){
for (l = 0; l < miniatury.length; l++) {
if (miniatury[l].className.match("zoom") != null ) {
var href = miniatury[l].href;
if (document.images) {
preImg[l] = new Image();
preImg[l].src = href;
miniatury[l].setAttribute("onclick", "przybliz(preImg["+[l]+"].src, event); event.returnValue=false; return false;");
}
else {
miniatury[l].setAttribute("onclick", "przybliz(href, event); event.returnValue=false; return false;");}
}
}
}
function przybliz(adres, event) {
pojemnik.style.display = 'block';
if (navigator.appName == "Microsoft Internet Explorer") {
pozycjaX= window.event.clientX + document.documentElement.scrollLeft
+ document.body.scrollLeft;
pozycjaY= window.event.clientY + document.documentElement.scrollTop
+ document.body.scrollTop;
}
if (navigator.appName != "Microsoft Internet Explorer") {
pozycjaX = event.clientX + window.scrollX;
pozycjaY = event.clientY + window.scrollY;
}
pojemnik.style.top = pozycjaY+'px';
pojemnik.style.left = pozycjaX+'px';
Question is:
How to change the code into
onclick = callback(f, arguments)
with passing event object values, and having the luxury to use them later ?

Well, doing it with jQuery:
$(miniatury[l]).bind("click", preImg[l], przybliz);
After which you could retrieve it in the function:
function przybliz(evt) {
var adres = evt.data;
//...
}
Without jQuery it becomes a bit more difficult, since you might have to store the value in a closure, which unless you're careful can force the whole scope chain to stay in memory (not a good thing).

The best way is to just append a handler to it. eg:
if (minitury.attachEvent) {
minitury.attachEvent("onclick", callback);
} else {
minitury.addEventListener("click", callback, false);
}
Where callback is a function with a single parameter. Like below:
function callback(evt) {
if (! evt) evt = window.event;
<insert other code here>
}
This should be what you're looking to do.
EDIT: I realized you were asking how to send parameterized callbacks. I doubt there is a good cross-browser method of doing this, but you could get around that by adding a custom attribute to the element in question which holds your data and through the event object (either evt.target or evt.srcElement) you can access the element. Make sure to follow the guidelines set by the w3c for custom attributes. w3c custom attributes

IE6 is pretty bad that way. Getting familiar with raw JS is essential, but ultimately you won't want to be bothered with making all kinds of accommodations for the little differences between browsers.
Antony Mills shows you how easy a framework (like jQuery or prototype) can make your life. Another key thing about frameworks is that they have usually thought long and hard about all of these cross browser issues so that you don't have to.

Related

AttachEvent Not Working on IE11

I am trying to make two checkboxes, out of which only one can be selected at any point of time. I searched the forums a lot and found a few suggestions.
if (document.attachEvent){
// For IE Browsers.
document.attachEvent("DOMContentLoaded", function (event) {
var saSelector = document.querySelector('input[name=saWrite]');
var cgSelector = document.querySelector('input[name=cgWrite]');
if (cgSelector !== null) {
cgSelector.attachEvent('change', function (event) {
if (cgSelector.checked) {
document.querySelector('input[name=saWrite]').checked = false;
}
});
}
if (saSelector !== null) {
saSelector.attachEvent('change', function (event) {
if (saSelector.checked) {
document.querySelector('input[name=cgWrite]').checked = false;
}
});
}
});
}
I wrote a similar function with addEventListener in place of attachEvent for non-IE browsers. That works on Firefox. But this method somehow doesn't work for IE. Am I doing something wrong here? Any suggestions would be helpful. I wish i could use JQuery for this. But i cant.
https://jsfiddle.net/20g7ym8q/
You say you want to use JQuery but you can't. I realize starting out that may seem like a real limitation, but it isn't. Anything you can do with JQuery you can do with JavaScript.
Your code won't work on IE11 because attachEvent has been deprecated and removed in favor of accepting addEventListener as the standard way to attach an event in all modern browsers. If you're looking for generational support without JQuery and without code duplication, setting up your own Object to use as an intermediate layer between your code and the browser is probably the best way to go about this.
function $(ele) {
return {
ele: document.querySelector(ele),
on: function(ev, fn) {
(document.attachEvent) ?
this.ele.attachEvent(ev, fn) :
this.ele.addEventListener(ev, fn);
},
checked: function(change) {
if(typeof change !== undefined) this.ele.checked = change;
return this.ele.checked;
}
}
}
The above is a function that returns an Object with two methods and a property. It works similarly to JQuery for familiarity and consistency, but it is without the overhead of including the entire JQuery library.
The methods allow you to add an event using .on with an event type and function as parameters. The methods also allow you to set or get the checked property of the specified element. .checked() will simply return a boolean as to whether the box is checked, .checked(boolean) will set the elements property to the desired state.
In practice, to solve your dilemma of only one allowable check box, you could do this:
var sa = $('input[name="saWrite"]');
var cg = $('input[name="cgWrite"]');
cg.on('click', function(ev) {
sa.checked(false)
});
sa.on('click', function(ev) {
cg.checked(false);
});

JavaScript window.scrollTo Alternative

I'm writing a specific javascript plugin for a specific website.
In one of my methods i want to use window.scrollTo but unfortunately the site owner has overridden the native functionality of this function (also window.scroll).
So i'm thinking about 2 possible solutions:
Write my own myScrollTo implementation
Somehow detect if the native function is overridden and call the native function.
If you have any ideas on this, i will be very glad to hear them :)
Thanks.
Well you can create an iframe and use its window instance to get the native implementation of scrollTo. The following code should work
var iframe = document.createElement('iframe');
iframe.style.display = 'none';
document.body.appendChild(iframe);
window.altScrollTo = iframe.contentWindow.scrollTo;
Now you should be able to use it as below
aScrollTo.call(window, 0, 600)
Well it's not the perfect solutions but is suites my current needs.
First i will check if the native function is overridden with this helpful piece of code :
function isFuncNative(f) {
return !!f && (typeof f).toLowerCase() == 'function'
&& (f === Function.prototype
|| /^\s*function\s*(\b[a-z$_][a-z0-9$_]*\b)*\s*\((|([a-z$_][a-z0-9$_]*)(\s*,[a-z$_][a-z0-9$_]*)*)\)\s*{\s*\[native code\]\s*}\s*$/i.test(String(f)));
}
Source: https://stackoverflow.com/a/7536972/3009194
Then i will try the alternatives : window.scroll as the is no difference between window.scroll() and window.scrollTo()
And finally if this one is also overridden, i guess i will use document.body.scrollTop
Yes i know, there is a possibility that the body is not the scrolling Element.
Unfortunately the document.scrollingElement is still a draft and not supported in most browsers.
So the final code will look something like this:
function myScroll(left, top) {
if (isFuncNative(window.scrollTo)) {
window.scrollTo(left, top);
} else if (isFuncNative(window.scroll)) {
window.scroll(left, top);
} else {
document.body.scrollLeft = left;
document.body.scrollTop = top;
}
}
myScroll(0,150);

Javascript .focus() not working

I'm having trouble getting the javascript .focus() function to work. At this point, I'm really not sure why it's failing to execute. Could it have something to do with onload?
This is my code:
var request_brochure = function()
{
var name = $("name").value;
var email = $("email").value;
var isValid = true;
if(name == "")
{
$("name").focus();
alert("Please fill in your name.");
$("name").focus();
isValid = false;
}
else if(email == "")
{
window.setTimeout(function(){$("email").focus();}, 100);
alert("Please fill in your email. ");
window.setTimeout(function(){$("email").focus();}, 100);
isValid = false;
}
if(isValid)
{
$("form_b").submit();
//$("brochure").innerHTML = "Request Submitted";
}
}
window.onload = function () {
$("name").focus();
$("submit1").onclick = request_brochure;
}
var $ = function (id) {
return document.getElementById(id);
}
Any help is appreciated
I suspect the event isn't binding correctly. window.onload does not ensure the element with id submit_1 is loaded. try adding the event to the button directly:
<input id="submit_1" onclick="request_brochure()" />
if that works then your problem is just that. Without jQuery you could mostly get away with binding the event by putting $("submit1").onclick = request_brochure; in a <script> at the end of the page, but results will be mixed from browser to browser. I'd really recommend using jQuery(function () { $("submit1").onclick = request_brochure; }) and leaving the heavy lifting of determining when the page is completely loaded to the library, since it's been tried and tested for years and is much less likely to fail than a native approach.
The comments on your question have a lot of truth, and I'd hate to see you go down the path of jQuery's high level stuff designed to make non-programmers and programmers with little javascript knowledge live's easier. With that said, the browser landscape is quite the jungle, with more than half of the world on IE 8 or lower (I could be wrong about that by now, but I'm sure it's still a considerable amount), jQuery's low level stuff (like $.ajax, $(function () {}) and the sizzle selector engine) is indispensable in my opinion. The main purposes of these utilities (jQuery's low level functions) is to address browser fragmentation issues and make devs live's easier. It's good to know the underlying code, but I'll take $.ajax over any implementation of HttpXmlRequest in a heartbeat (I still remember the days of IE6's activeX component... those were dark times). just don't do $('#some-form-field').val() without first learning document.getElementById('some-form-field').value and you should be fine :)

Code works as an Opera UserJS, but gives "undefined" errors in Chrome and Greasemonkey userscripts

I am trying to write a cross-browser userscript that hides IMDb's rating and replaces it with a link that allows the user to rate the show/film later on.
In Opera, this approach already works, but Firefox and Chrome both stumble upon an undefined function and/or variable. here's my code so far:
function hideimdbratings() {
if (document.getElementsByClassName("star-box")[0]) {
reenabled = document.getElementsByClassName("star-box")[0].innerHTML;
document.getElementsByClassName("star-box")[0].innerHTML = 'Rate!';
}
}
function reenableit() {
document.getElementsByClassName("star-box")[0].innerHTML = reenabled;
}
window.addEventListener('load', hideimdbratings, false);
The first part works fine, hideimdbratings() is executed and hides the rating.
But, upon clicking the "Rate!"-link, Firefox and Chrome both say that reenableit() is not defined.
Trying this:
onclick="document.getElementsByClassName(\"star-box\")[0].innerHTML = reenabled;"
Results in them saying reenabled is not defined.
I tried putting the code directly into the event listener:
window.addEventListener("load", function(e) {
// ...
}, false);
and this way of defining the functions (with unsafeWindow for Firefox):
var window.reenableit = function() { }
But, whatever I do, both reenableit() and reenabled remain undefined. From what I understand, neither the function nor the variable are global in browsers other than Opera, but I can't seem to find a solution just yet.
What am I missing/doing wrong?
That code won't work in Chrome because Chrome userscripts operate in a sandbox ("isolated world"), and you cannot set or use page-scope javascript objects in Chrome. And, Chrome does not fully/properly support unsafeWindow.
The code would work in Firefox+Greasemonkey with careful use of unsafeWindow, but that is not recommended here (and won't help with Chrome).
The classic approach, when one needs to interact with page-scope javascript in a cross-browser way is to use Script Injection. This is the only thing that works well in Chrome.
However, the smartest thing to do is not use page-scope JS at all if you don't have to. And, for what's in this question, you don't need to. (Hint: never use onclick or similar attributes! Always use addEventListener(), or equivalent.)
Refactoring the code to avoid leaving the sandbox scope, it becomes:
function hideImdbRatings () {
var oldStarBoxHTML;
var starBox = document.getElementsByClassName ("star-box");
if (starBox.length) {
starBox = starBox[0];
oldStarBoxHTML = starBox.innerHTML;
starBox.innerHTML = 'Rate!';
document.getElementById ("showVote").addEventListener (
"click",
function () {
//-- "this" is a special javascript scope.
this.innerHTML = oldStarBoxHTML;
}
);
}
}
window.addEventListener ('load', hideImdbRatings, false);
However 2, you'll notice that all of the code so far, busts the interaction of IMDB's rating widget. This is because it's overwriting innerHTML, which trashes the widget's event handlers. Don't use innerHTML like that.
The smartest-er thing to do is to hide the block, similar to Geo's answer, like so:
function hideImdbRatings () {
var starBox = document.getElementsByClassName ("star-box");
if (starBox.length) {
starBox = starBox[0];
starBox.style.display = 'none';
var rateLink = document.createElement ('a');
rateLink.id = 'showVote';
rateLink.href = '#';
rateLink.textContent = 'Rate!';
starBox.parentNode.insertBefore (rateLink, starBox);
document.getElementById ("showVote").addEventListener (
"click",
function () {
//-- "this" is a special javascript scope.
this.style.display = 'none';
starBox.style.display = 'block';
}
);
}
}
window.addEventListener ('load', hideImdbRatings, false);
There is an additional factor that may stop the script from working in Chrome. By default, Chrome userscripts may run after the load event. To work around that, specify #run-at document-end in the metadata block of your script.
I couldn't figure out how to make the variable or the function global, though here's a work around that should work just fine for you:
function hideimdbratings() {
if (document.getElementsByClassName('star-box')[0]) {
// hide the "star-box" container
var b = document.getElementsByClassName('star-box')[0];
b.style.display = 'none';
// add the "Rate!" link
var n = document.createElement('a');
n.setAttribute('href', 'javascript:void(0);');
n.setAttribute('onclick', 'document.getElementsByClassName("star-box")[0].style.display = "block"; this.style.display = "none";');
n.innerHTML = 'Rate!';
b.parentNode.insertBefore(n, b.nextSibling);
}
}
window.addEventListener('load', hideimdbratings, false);
Aha! Found it. Try this:
var uw = (this.unsafeWindow) ? this.unsafeWindow : window;
var b = document.getElementsByClassName('star-box')[0];
uw.reenabled = b.innerHTML;
b.innerHTML = 'Rate!';
Inspired by this post

event.srcElement in firefox

I have some old code that I'm updating to work in firefox, and I've run across a problem.
In the code, there's a function that looks like this:
function tableEnter() {
myLocation = event.srcElement;
}
This doesn't work in firefox. I've researched this quite a bit, but most of the solutions I've found require an event to be passed to this function, and then act on the e parameter that was passed. ...Unfortunately in the code that I'm updating, I'm not getting any parameters passed.
What's the solution to making the event.srcElemet work in firefox, without any parameters being passed to my function?
Edit:
Okay, the question is becoming: How do i pass the event object to my tableEnter() function?
Here's what the code is currently doing:
$(document).ready(function () {
//make table rows
//for every new table row...
myRow.onmouseover = tableEnter; (this is probably a bad name. it should be like..rowEnter. But this is the way I found the code)
});
the question is now, how do i pass the event object into tableEnter() so that I can do the things suggested on the internet, and the answers below.
Thanks.
I honestly can't see why you would want to have access to the event object, without it being an argument of your handler. But, hey, that's just me. Although no parameters are specified, FF and chrome just do as they please and pass the event object to the handler anyway. So:
function tableEnter()
{
var evt = window.event || arguments[0];
var src = evt.target || evt.srcElement;
}
And that's it. Personally, I would advise you to do what the whole wide web is doing:
function handler(e)
{
e = e || window.event;
var target = e.target || e.srcElement;
}
Since you're using FF, you shouldn't try to diverge from any of the coding conventions or standards too much, however vague they might be. At least it's better than no standards or conventions at all.
In view of your update, just change your function definition to:
function tableEnter(e)
{
//though in jQuery, I suspect e is allready X-browser-proof
e = e || window.event;
//just leave ^^this^^ line out, and check in IE: alert(typeof e);
//if it alerts object, jQuery passed the event object for you
var theRow = this;//<-- this points to the row that triggered the mouseover
var jQRow = $(this);//gives you access to jQuery methods
var target = e.target || e.srcElement;//<-- mainly of importance in delegation
}

Categories

Resources