second this is wrong? [duplicate] - javascript

I'm not quite sure if I'm not using this in the correct scope or what, but I have a script that basically captures a link click and causes the page to fade out before going to the linked page. However, if the link is a JavaScript onclick, the script fails.
Here's my code:
<script type="text/javascript">
pageObj = {
init: function(){
$("body").fadeTo("slow", 1);
},
redirectPage: function(redirect){
window.location = redirect;
},
linkLoad: function(location){
$("body").fadeOut(1000, this.redirectPage(location));
}
};
$(document).ready(function() {
pageObj.init();
$("a").click(function(e){
e.preventDefault();
if (this.attr('onclick') !== undefined) {
eval(this.attr('onclick').val());
} else {
var location = this.href;
pageObj.linkLoad(location);
}
});
});
</script>
As you can see, I'm trying to do a check to see if the link has the onclick attribute, and then call the onclick function if it exists. How can I achieve this?

Use: $(this).attr instead of this.attr
This forces it into the context of jQuery.

While Diodeus is correct that you need to wrap this in a jQuery collection before using attr() (it's a method of a jQuery collection, not of an HTMLElement), you can just as well skip attr().
$("a").click(function(e){
var location;
e.preventDefault();
if ($.isFunction(this.onclick)) {
this.onclick.call(this, e);
} else {
location = this.href;
pageObj.linkLoad(location);
}
});
Note that I used the property (when an HTML document loads, attributes are usually preloaded into properties, with on_______ attributes being preloaded as methods. Also note that I used this.onclick.call() rather than eval(), setting the correct this for onclick methods, and ensuring access to the event object as an argument.

Related

If element is not present on page why is JavaScript throwing an error?

My scenario is that I have one JS file which is being used on two HTML pages. One has a button with id="a" and the other doesn't have it. I have attached an event on id="a" by doing this.
document.getElementbyId("a").onclick = function () { .. }
My problem is when I run second file which doesn't have the button with id="a" and it throws the error
TypeError: document.getElementbyId(...) is null
which causes some unexpected behaviour! Is there any way to keep using just one JS file or should I separate the JS file for every html page?
I read a question on StackOverflow here which is about why jQuery doesn't throw an error if the selector is invalid. I need same or similar functionality with vanilla JavaScript.
The simple fix is to add a check:
if (document.getElementById("a") !== null) {
document.getElementById("a").onclick = function () /* .. */ };
}
jQuery will never break on missing elements, it just silently fails. You can argue about whether that is good or not.
The trick jQuery uses is that it will always return something. $("#id") will return a jQuery wrapper. That wrapper has functions like .on and .load and .html. A selector can match one, more or no elements. For the wrapper it doesn't matter how many elements are matched.
It works like this:
var elem = get_element("a");
elem.onclick(callback);
function get_element(id) {
var elem = document.getElementById(id);
return { // return a wrapper
onclick: function (callback) {
if (elem !== null) {
elem.onclick = callback;
} else {
// silently fail
}
}
};
}
Consider testing existance
var elm = document.getElementById("a");
if (elm) elm.onclick = function () { .. };
Or falling back in the case of a falsy
(document.getElementById("a") || {}).onclick = function () { .. };
In this second method, the function still gets set but will be quickly GC'd as the reference to {} dies by the next line.
All of the above solutions are correct. However, you should definitely move away from writing whatever.onclick = function as that is no longer the best way to write your code. Using this method, you only get one click event. Using a more modern approach allows you to attach as many events as you want.
Also, you mention jQuery but there is no jQuery in the supplied code only vanilla JavaScript. Do you need a jQuery solution or JavaScript?
JavaScript
document.addEventListener('DOMContentLoaded', function(){
var elm = document.getElementById('a');
if ( elm ) { //if the element exists add the click event
elm.addEventListener('click', function(){
//Your "onclick" code here
});
}
});
jQuery
$(function(){
$('#a').on('click', function(){
//Your "onclick" code here
});
});

Each function not working as expected when using custom function

Consider this code running on page ready:
$("input.extraOption[checked]").each(function() {
console.log($(this));
$(this).closest('.questionRow').find('.date').attr("disabled", true);
$(this).closest('.questionRow').find('.dateSpan').hide();
$(this).closest('.questionRow').find('.date').val("");
$(this).closest('.questionRow').find('.textareaResize').attr("disabled", true);
$(this).closest('.questionRow').find('.textareaResize').val("");
$(this).closest('.questionRow').find('.text').attr("disabled", true);
$(this).closest('.questionRow').find('.text').val("");
$(this).closest('.questionRow').find('.checkbox').attr("disabled", true);
});
I want to refactor these calls as they are used elsewhere as well, so I created the following function:
jQuery.fn.extend({
toggleAnswers: function (disable) {
var group = $(this);
group.find('.date').attr("disabled", disable);
group.find('.date').val("");
group.find('.textareaResize').attr("disabled", disable);
group.find('.textareaResize').val("");
group.find('.text').attr("disabled", disable);
group.find('.text').val("");
group.find('.checkbox').attr("disabled", disable);
if(checkedStatus === true){
group.find('.dateSpan').hide();
}else{
group.find('.dateSpan').show();
}
return group;
}
});
I then proceed to changing the 8 $(this).closest(...) calls with:
$(this).closest('.questionRow').toggleAnswers(true);
Here's the problem: on a page with 5 elements that match the selector, only the first one suffers the changes (in other words I only get one console.log)! Before the refactor I get the expected change in all 5 elements.
What is being done wrong in this refactor?
checkStatus isn't defined anywhere, causing an exception. You seem to want to use disable instead.
On a side note, this already refers to the jQuery collection that this method is called on, so wrapping this in a jQuery object ($(this)) is redundant/unnecessary. Note that this is specifically inside of a $.fn method, not normal jQuery methods. For example, inside event handlers, this refers to the DOM element, so you need to wrap it in $(this) in order to call jQuery methods on it.
Also, disabling an element should be done with .prop("disabled", true/false): .prop() vs .attr()
You can also combine any selectors that you call the same jQuery method on. For example, group.find('.date').val(""); and group.find('.text').val(""); can be combined into: group.find(".date, .text").val("");
Putting all of those suggestions together, as well as iterating over this (for consistency and scalable sake), here's what I'd use:
jQuery.fn.extend({
toggleAnswers: function (disable) {
return this.each(function (idx, el) {
var $group = $(el);
$group.find(".date, .text, .textareaResize, .checkbox").prop("disabled", disable);
$group.find(".date, .textareaResize, .text").val("");
$group.find(".dateSpan").toggle(!disable);
});
}
});
And depending on how you use it, I'd set it up like:
var targets = $("input.extraOption[checked]"),
toggler = function () {
$(this).closest(".questionRow").toggleAnswers(this.checked);
};
targets.each(toggler).on("click", toggler);
DEMO: http://jsfiddle.net/XdNDA/

SWFAddress: adding "/" char at the end of an URL via JavaScript

how can I add the / char at the end of the URL without force the user to write it?
I'm starting to work with SWFAddress using JavaScript and jQuery directly (without Flash) like this wonderful site.
So something like this:
http://mysite.com/section
// and after click to a specific <a ="#">button</a> get
http://mysite.com/section/#/sub-content
// instead of
http://mysite.com/section#/sub-content
I've started with this code to do it:
$(".time_dot, .time_year").click (function () {
onClickEvent (this);
});
function onClickEvent (dom) {
var timer = setTimeout (function () {
SWFAddress.setValue($(dom).parent().attr("id"));
}, 200);
// do something else
}
The setTimeout method is used to avoid the <a> button overwrite the SWFAddress.setValue method, but I don't know a way to change the URL address to url.com/content#/... to url.com/content/#/....
How can I do that?
As already said, you cannot change the URL without forcing a reload of the site. And I don't think you want that. (but it depends on how SWFAddress.setValue() actually works, if it just keeps track of the URLs internally (without changing the browsers URL) then you can do it)
But I wanted to give you an alternative for setTimeout:
$(".time_dot, .time_year").click (function (event) {
event.preventDefault();
event.stopPropagation();
onClickEvent (this);
});
function onClickEvent (dom) {
SWFAddress.setValue($(dom).parent().attr("id"));
// do something else
}
With this you prevent the click event from bubbling up and also prevent the change the default action (which would be following the link).
See jQuery event object.
You can't change the "path" part of the URL without reloading the page. So, if you're at http://mysite.com/section, you can not navigate to http://mysite.com/section/#/sub-content without reloading the page.
you mean like
$(".time_dot, .time_year").click (function () {
setTimeout (function () {
SWFAddress.setValue("/"+$(this).parent().attr("id")+"/");
}, 200);
});
function fixUrl(url) {
return url.replace(/\/?$/,'/');
}
//or
function fixUrl(url) {
return url.match(/\/$/)
? url
: url + '/';
}

MooTools: Attaching event to multiple elements

I've got a jQuery routine I need to convert to MooTools, but I can't get it to work. Here's my jQuery version:
$(".google-analytics-link").click(function () {
var href = $(this).attr("href");
pageTracker._link(href);
location.href = href;
return false;
});
Here's my MooTools translation:
$$(".google-analytics-link").addEvent("click", function () {
var href = this.get("href");
pageTracker._link(href);
location.href = href;
return false;
});
Doesn't seem to work though. I don't understand MooTools selectors. Any help, please?
You don't need to explicitly set the window's location when clicking the link already does it. Currently the code stops the native event, calls a method on the pageTracker object, then redirects to the location of the clicked link.
Google Analytics documentation for the _link method says that
This method works in conjunction with the _setDomainName() and _setAllowLinker() methods to enable cross-domain user tracking. The _link() method passes the cookies from this site to another via URL parameters (HTTP GET). It also changes the document.location and redirects the user to the new URL.
implying that you simply have to stop the click event, and call the _link method which will take care of the rest.
var analyticsLinks = document.getElements('.google-analytics-link');
analyticsLinks.addEvent('click', function(event) {
// stop the page from navigating away
event.stop();
var href = this.get('href');
// let the Analytics API do its work, and then redirect to this link
pageTracker._link(href);
});
$$(".google-analytics-link").each(function (e) {
e.addEvent("click", function () {
var href = this.get("href");
pageTracker._link(href);
location.href = href;
return false;
});
});

Wait for iframe to load in JavaScript

I am opening an iframe in JavaScript:
righttop.location = "timesheet_notes.php";
and then want to pass information to it:
righttop.document.notesform.ID_client.value = Client;
Obviously though, that line isn't going to work until the page has fully loaded in the iframe, and that form element is there to be written to.
So, what is the best/most efficient way to address this? Some sort of timeout loop? Ideally I would really like to keep it all contained within this particular script, rather than having to add any extra stuff to the page that is being opened.
First of all, I believe you are supposed to affect the src property of iframes, not location. Second of all, hook the iframe's load event to perform your changes:
var myIframe = document.getElementById('righttop');
myIframe.addEventListener("load", function() {
this.contentWindow.document.notesform.ID_client.value = Client;
});
myIframe.src = 'timesheet_notes.php';
Again, this is all presuming you mean iframe, not framesets.
I guess you can pretty easily do this with jQuery... jQuery Home
Just hook the page to the jQuery $ function ()... e.g.
$(document).ready(function() {
$('iframe').load(function() {
// write your code here....
});
});
Have a quick look at the file uploader example here:
Using iframes for multiple file uploads...
iFrame could have dynamically loaded elements and the best option to work with them is to use recursion:
$('iframe').ready(function() {
var triggerMeAgainIfNeeded = function() {
setTimeout(function() {
var neededElm = $('.someElementThatLoadedAfterIframe');
if (neededElm.length > 0) {
// do your job
} else {
triggerMeAgainIfNeeded();
}
}, 10);
}
});
try this one...
$('iframe').each(function() {
$(this).ready(function() {
$('#result').html("ready");
});
});

Categories