What is the best practice for writing bookmarklets - javascript

I am writing some bookmarklets for a project that I am currently working on and I was wondering what the best practice for writing a bookmarklet was. I did some looking around and this is what I came up with
javascript:void((function()
{
var%20e=document.createElement('script');
e.setAttribute('type','text/javascript');
e.setAttribute('src','http://someserver.com/bookmarkletcode.js');
document.body.appendChild(e)
})())
I felt this is nice because the code can always be changed (since its requested every time) and still it acts like a bookmarklet. Are there are any problems to this approach ? Browser incompatibility etc? What is the best practice for this?

That bookmarklet will append a new copy of the script to the document every time it is run. For long-lived pages (e.g. Gmail), this could add up to a lot of memory usage, and if loading your script has side effects, they’ll occur multiple times. A better strategy would be to give your script an id, and check for existence of that element first, e.g.:
var s = document.getElementById('someUniqueId');
if (s) {
s.parentNode.removeChild(s);
}
s = document.createElement('script');
s.setAttribute('src', 'http://example.com/script.js');
s.setAttribute('type', 'text/javascript');
s.setAttribute('id', 'someUniqueId');
document.body.appendChild(s);
N.B. another alternative is to keep the existing script if it’s already in the document. This might save some server traffic if your bookmarklet is used frequently between page reloads. The worst case is that someone is using an older version of your script for a while; if you don’t expect it to change often, that might be fine.

Looks OK. But if your js file is already cached, it will not be requested every time. So you'd need it to append '?' + new Date() to your src attribute to ensure it is requested every time.

Related

Chrome doesn't take new CSS and Javascript programming

I'm having an issue with updating a web application in Chrome. It’s an ASPX site, but I don’t believe it’s relevant to this conversation. The issue is that our clients don’t get the latest programming in Chrome, because most of the time, it fails to release Javascript and CSS programming from memory, then proceeds to cause all kinds of mayhem while in use.
I tried placing this piece of code in the <head>, like many have suggested, but it doesn’t work all the time.
<script type="text/javascript">
function reload_js(par_src) {
src = $('script[src$="' + par_src + '"]').attr("src");
$('script[src$="' + par_src + '"]').remove();
$('<script/>').attr('src', par_src).appendTo('body');
}
reload_js("../../global/scripts/global_scripts.js");
reload_js("scripts/main.js");
reload_js("scripts/tabs/jquery.ui.core.js");
reload_js("scripts/tabs/jquery.ui.widget.js");
reload_js("scripts/tabs/jquery.ui.mouse.js");
reload_js("scripts/tabs/jquery.ui.sortable.js");
reload_js("scripts/tabs/jquery.ui.tabs.js");
reload_js("scripts/chat_room.js");
</script>
And I use this Javascript file to refresh my css's. One that I found here.
<script src="../../global/scripts/css_refresh.js" type="text/javascript"></script>
I'm scared that these methods cause unnecessary overhead, because they run every time a page is loaded.
Could one of you experts please tell me if this is the most efficient method for updating? Or perhaps recommend a better method to keep these files up to date?
Simplest way to fix this is to add an identifier to the script/style-files. Just add something like ?version=12 (and update the number for each release) then the browser will fetch the newest file. You can also use some sort of server-side date-option after the ?of course, to force a new fetch every day or similar.

Handling CSS load failure

I've been spending some thinking time recently on how to best handle resource failure for a page.
Of course with JavaScript files there isn't much 'clever' stuff that you can do. If you're loading from a CDN we can do something like this (taken from the HTML5 Boilerplate project):
<!-- Grab Google CDN's jQuery, with a protocol relative URL; fall back to local if offline -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="local/jquery-1.6.1.min.js">\x3C/script>')</script>
However I haven't seen much documented on CSS failure.
If your page is a blog post or something similar then best practice would be to make sure your markup is good, so that the un-formatted version is still a very much readable, if only a little boring, document.
But what if you don't want to fall back to Lynx style? There may be a good reason; for example your application is complicated and is very much dependant on your layout. In which case, showing the page with no stylesheet is probably worse for user experience than not showing it at all.
Therefore, I wondered if a solution such as this would work - and if any of you are using it (or something similar)?
Add a helper class at the end of your CSS file #test { line-height: 1; }
Add this JavaScript at the end of your page
var el = document.createElement('div');
el.setAttribute('id', 'test');
document.body.appendChild(el);
var o = (el.currentStyle) ? el.currentStyle['line-height'] :
(window.getComputedStyle) ? document.defaultView.getComputedStyle(el, null).getPropertyValue('line-height') : null;
if (o !== '1px') {
document.body.innerHTML = '<p>Resources failed to load, a prettier message should go here</p>';
}
See this JS Fiddle for it all together.
Questions are
Thoughts on the above?
Are you using something like this?
Do you think its a good pattern (note that I'm only advocating this for web apps that are reliant on their styles).
Your rule may not take due to user stylesheets or other browser settings overriding the rule you're testing on. line-height seems especially vulnerable to this, plus also any rule that takes a length value runs the risk of returning a computedStyle in a different unit to the one you expected, making the string comparison fail. (Not to mention the 1/1px confusion in your example.)
In general I think this needs to fail better. If you think styles haven't loaded, add a warning to the page by all means, but don't replace the whole page with an error, making it completely useless to someone who might have been able to at least try to use it. This fragility is bad for accessibility.

Using jquery in third party js code

First of all I am new to javascript so this question might be stupid.
I have a requirement where i would provide a js script to other websites to include in their webpages.
Now my question is should I use jquery or plain javascript.Will/Can jquery effect the rest of the functionality of the site.
Plus what is the benefit I will get using jquery over plain javascript.
My requirement is to get all images of the website and do some processing on the images on these websites.
Thanks in Advance.
I would say, if the processing does not involve jQuery then do not use it.
You need to deal with the following issues if you include it
failure to load (for network reasons)
conflict with other versions locally loaded
conflict with other libraries locally loaded
All issues have workarounds/solutions but you have to implement each an everyone of those.
If you just need to find all images in page, then you can use the .getElementsByTagName() method
var imagelist = document.getElementsByTagName('img');
and just do the processing on that..
You might need of-course to attach your code at the load event to be sure that whatever your code does, the DOM is ready to accept it..
for modern browsers that would be .addEventListener(), while for IE it would be .attchEvent()
your script could be something like this
(function(){ // wrap in a self-invoking function to avoid global name-space pollution
// attach to the load event so out code runs once the page has loaded
if (window.addEventListener) { // modern
window.addEventListener('load', imageProcessor, false);
}
else if (window.attachEvent) { // IE
window.attachEvent('onload', imageProcessor );
}
function imageProcessor(){
// get all images
var imagelist = document.getElementsByTagName('img');
// loop the list of images to do something with each image
for (var img = 0; img < imagelist.length; img++)
{
var currentImage = imagelist[img];
// do processing on the currentImage which hold a reference to the image
}
}
})(); // do the self-invoking
demo: http://jsfiddle.net/gaby/nXPzk/
this would be much more easier to achieve with jQuery, mostly because you want to be able to distribute your script to any browser(and jQuery is a cross-browser library), but it does have it's downsides.
If it's your own project, your own page, you know exactly what and when to load/execute, but if you're giving your script to other folks, you have to think of 3 problems at least:
different libraries conflicts
This can be resolved using jquery's noConflict method, but I've seen it fail in some circumstances
same library conflicts
Say that the user already has loaded version X of jQuery. You oblige him to load version Y too, and besides the fact that this would be very inefficient, it may cause conflicts between different jQuery versions.
There are workarounds for this( to check if jQuery exists and load it asynchroniously if it doesn't) but the code gets a bit more complicated.
code size
When i think of a third party piece of code, I'm thinking...hmm a few kb to load to be able to run a function, but if you want to use jQuery, you may be forcing the one who uses your script to load a whole library just for your one functionality instead of a 1-2 kb (needles to say what the implications are if the user has a slow connection)
Having both the ups and downs in mind, it really depends on what are you doing, so, as Gaby aka G. Petrioli said, " if the processing does not involve jQuery then do not use it".
JQuery is much more convenient than plain javascript. You don't have to worry about cross-browser compatibility (e.g. IE vs. Firefox vs. Chrome). I would recommend this over javascript unless you are want to handle that yourself :)
You can load in the Jquery library, but that might be a little heavy than what you're looking for. Also, you will need to fetch the library from a server, which adds another point of failure (however, if you host this on your own server, then you will have more control).
TLDR: JQuery is convenient. If you have access to it, then you should use it.

IE equivalent for GM_setValue and GM_getValue (Greasemonkey storage)

I have made a script that runs with no glitch on Firefox. I'm retrieving some data from external domain in an iframe to insert them in the page by using setInterval()
I have tried to use Trixie, so that it runs in IE, but it seems that functions GM_getValue and GM_setValue were not defined.
I've added these replacement functions, based on cookies, but I can't get it to work in a cross-domain way : http://www.howtocreate.co.uk/operaStuff/userjs/aagmfunctions.js
The cookie is created, and the data stored, but it's only accessible from the iframe, not from the top document.
Here is the basic structure I used : http://www.pastie.org/1889407
In test() I have access to the value stored with GM_getValue("destination",""), but it doesn't work inside function check().
1) Is there a way to make the cookie cross-domain?
2) Are there other ways to store data in IE in a cross-domain way? (I have briefly heard of Flash objects, but it doesn't seem quite a light solution...) Other implementations of these functions (getValue and setValue) are quite hard to find.
3) I'm using Trixie, maybe it's not the best solution, any advice on what plugin I should better use, to have those functions?
Well, after trying many solutions, I finally found an answer.
I used IE7PRO, that includes functions PRO_setValue and PRO_getValue, that ended to work just fine.
My problem was that I tried to access the external domain on an iframe, which is not supported by IE7PRO apparently.
I just splitted my script into 2 user scripts : one for my domain, that check if new PRO_getValue are available, and the other one for the external domain, that saves the data. One tab for my page, one for the other and surprisingly, it worked like a charm.
It doesn't seem possible with Trixie or GM4IE, the only inconvenient is that IE7PRO is heavy, since the GM script support is just a small part of this extension.

Add a bookmark that is only javascript, not a URL

I'm thinking that the reason I can't do this is because it might be a huge security hole, but here goes...
I want to have a bookmark on my browser (FF3, preferably) that is only a snippet of javascript code. It would merely construct a URL based on the current date and then do a window.location = on that URL.
I know that I could make a page and keep it on my local machine and just refer to it that way, but I was just wondering if you could bypass that step and actually have the "location" of the bookmark really just be javascript. I could have sworn that this was possible years ago, but I can't find anything that tells me either way now.
What you want is a bookmarklet they are easy to create and should work in most major browsers.
Edit: Stack overflow seems not to allow creating bookmarklets in the context of the site, basically you can create a new bookmark and type the following in the location field
javascript:window.location='http://www.google.com/search?q='+Date()
to get a bookmarklet that searches google for the current date.
It is worthy of note that you can put that in a function wrapper as well. imranamajeed nicely illustrated that for us... but apparently I'm too new to the site to up his post. :P
so for clarity:
javascript:(function(){
location.href = location.href + "#";
})();
(the carriage returns did not affect performance in chrome and IE)
One minor catch. IE can only handle a 508 character URL in this format. If you save it in IE with a url longer than this, it will truncate without warning and thus fail.
If you need a really complex script, you'll need to use a "hosted" bookmarklet, where you have a short bookmark that injects a script tag into the page, to "call" your hosted bookmarklet.
It isn't as nice/portable, but its the only workaround.
Google Bookmark
javascript:(function(){var%20a=window,b=document,c=encodeURIComponent,d=a.open("http://www.google.com/bookmarks/mark?op=edit&output=popup&bkmk="+c(b.location)+"&title="+c(b.title),"bkmk_popup","left="+((a.screenX||a.screenLeft)+10)+",top="+((a.screenY||a.screenTop)+10)+",height=420px,width=550px,resizable=1,alwaysRaised=1");a.setTimeout(function(){d.focus()},300)})();
Well, I just created a bookmark in FF3, went back and updated it and added the following test:
javascript:alert('Wacky%20test%20yo');
Low and behold, after I saved and loaded, I was able to get my alert.
I'm sure you can work up something similar for your needs.

Categories