Home Download Buy Blog Forum Support

Sunday JS

Sunday JS

Postby agibsonsw on Sat Jun 23, 2012 11:04 pm

Here's a pointless JavaScript bookmarklet to play with on a Sunday. If you select some text on a web page and click the bookmarklet it will add a comment area under the text :D

Code: Select all
javascript:(function(){var d=document;var wrap=d.createElement('span');wrap.style.position='relative';var cmt=d.createElement('textarea');cmt.style.backgroundColor='yellow';cmt.style.top='1.5em';cmt.style.position='absolute';cmt.style.zIndex='99';cmt.style.height='4em';if(window.getSelection){var sel=window.getSelection();if(sel.rangeCount){var rng=sel.getRangeAt(0).cloneRange();rng.surroundContents(wrap);sel.removeAllRanges();sel.addRange(rng);wrap.insertBefore(cmt,wrap.firstChild);}}return false;})()


It won't work for IE < 9, although I could modify it so that it does.

The comment(s) will disappear if you refresh the page. Pretty pointless - although, if you're reading a detailed page it could be handy to jot a few notes down as you progress, I suppose :?:

If you want instructions to set this up come back to me. Andy.
Attachments
bookmk1.png
bookmk1.png (30.02 KiB) Viewed 2228 times
"I'm here to save your life. But if I'm going to do that, I'll need total uninanonynymity." Me Myself & Irene.
agibsonsw
 
Posts: 901
Joined: Fri Jan 27, 2012 9:11 pm

Re: Sunday JS

Postby SeanWcom on Mon Jun 25, 2012 2:41 pm

Neat! If you wanted to extend "Sunday JS" into Monday JS, here's an idea for ya... use HTML5 storage to store your notes so they persist on refresh. I'm not sure how well this would work with a bookmarklet, for example, on refresh the data would be in localStorage, but you'd have to click the bookmarklet to retrieve it. But might be worth exploring. :)

Here's a sample with working code (view source):
http://html5demos.com/storage
Multiple selections... one of my favorite ST2 features. View some noob-friendly screen casts here.
SeanWcom
 
Posts: 101
Joined: Wed Aug 03, 2011 1:31 am
Location: Georgia

Re: Sunday JS

Postby agibsonsw on Mon Jun 25, 2012 4:05 pm

@SeanWcom

Thank you: I need a "project" to help me study :) . I keep meaning to look at HTML5 in more detail, but have lacked motivation to study it "cold".

Andy.
"I'm here to save your life. But if I'm going to do that, I'll need total uninanonynymity." Me Myself & Irene.
agibsonsw
 
Posts: 901
Joined: Fri Jan 27, 2012 9:11 pm

Re: Sunday JS

Postby agibsonsw on Mon Jun 25, 2012 6:30 pm

Yes, if I give each comment a class-name of 'cmt' I can save them in localStorage:

Code: Select all
javascript:
(function(){
  var d=document;
  var els=d.getElementsByClassName('cmt');
  for(var i=0;i<els.length;i++){
    localStorage['cmt'+i]=els[i].value;
  }
  return false;
}
)()

I am able to then retrieve them later when I revisit the page:

Code: Select all
javascript:
(function(){
  for(var i=0;i<localStorage.length;i++){
    window.alert(localStorage[localStorage.key(i)]);
  }
  return false;
}
)()

Other than alerts I could append them to the body (bottom of the page) as paragraphs, or perhaps as another textarea that can be copied from.

However, I would ideally like to re-instate them as comments - at their original positions?! This is tricky.. How could I determine their original position?

I suppose I could store the word they were attached to and later find the first occurrence of this word.. I suppose :?:. Suggestions welcome, Andy.
"I'm here to save your life. But if I'm going to do that, I'll need total uninanonynymity." Me Myself & Irene.
agibsonsw
 
Posts: 901
Joined: Fri Jan 27, 2012 9:11 pm

Re: Sunday JS

Postby SeanWcom on Mon Jun 25, 2012 7:21 pm

Couple ideas off the top of my head... the first one, which would really only work on a fixed-width page, would be to store the comment's x/y position on the page (or the original text's position). But in a fluid width page, that location isn't guaranteed. Not the best idea unless you're looking for a quick fix.

The second idea, which is better than the first, would be to create a DOM query string for that text when you are saving the note. Something like this:

Code: Select all
var elements = $("*:contains('the original highlighted string')");


That will return an array of elements that contain that string. You'd have to account for multiple results. Maybe by getting that string's parent elements and creating a big selector all the way from body down... something that might ultimately look like "body div.content div.entry p:contains('asdf')". You might still have multiple results in that last paragraph element, but once you get down to the raw text, that's the best you can do.

So you'd eventually have that string that you could store in localStorage and reuse as your selector for that particular note.

There's obviously a lot of missing links here, but that might get an idea or two rolling in your head. :)

EDIT: I use jQuery in just about everything I do, and sadly for me, I've learned most of my Javascript knowledge by using jQuery. So, those selectors are certainly possible w/o the use of jQuery - but you're on your own there. :) .... although you can have your bookmarklet include external scripts (i.e., check for jquery already being loaded, if not yet loaded, add it to the page).

EDIT #2: Meant to post this originally and forgot. I've had it bookmarked for awhile and thought you might find it useful: http://dbushell.com/2012/05/22/javascript-bookmarklets/
Multiple selections... one of my favorite ST2 features. View some noob-friendly screen casts here.
SeanWcom
 
Posts: 101
Joined: Wed Aug 03, 2011 1:31 am
Location: Georgia

Re: Sunday JS

Postby agibsonsw on Mon Jun 25, 2012 8:02 pm

I seem to be in a minority, having studied JS before jQuery. I recommend it :). It took no time at all to learn jQuery, and I'm able to make use of pure JS when appropriate.

I can append a textarea to the body to list the comments:

Code: Select all
javascript:
(function(){
  var d=document;
  var newel=d.createElement('textarea'),str='Comments:\n';
  newel.id='thecmts';
  for(var i=0;i<localStorage.length;i++){
    str=str+(localStorage[localStorage.key(i)])+'\n';
  }
  newel.value=str;
  d.body.appendChild(newel);
  return false;
}
)()

[Annoyingly, the page I was using to test had no body tag, Doh!]

A variation on your query idea (querySelector() in JS) is to navigate backwards when adding the comment. That is, to navigate to the parent, parent,.. element. But I would also have to count sibling elements. That is, if the comment is within a paragraph, then I would need to determine which paragraph number it is.

I'll think a bit further.. there may be a way to get the comment "in the vicinity" of its original location. Otherwise, all this navigating is likely to get quite involved, and it's not likely to be 100% foolproof. Andy.

Just occurred to me. There should be a simpler way to navigate up to the parent, then body, and count the siblings (to the parent that is not the body).

Added: Thank you for that link ;)
"I'm here to save your life. But if I'm going to do that, I'll need total uninanonynymity." Me Myself & Irene.
agibsonsw
 
Posts: 901
Joined: Fri Jan 27, 2012 9:11 pm

Re: Sunday JS

Postby facelessuser on Mon Jun 25, 2012 8:15 pm

agibsonsw wrote:I seem to be in a minority, having studied JS before jQuery. I recommend it :). It took no time at all to learn jQuery, and I'm able to make use of pure JS when appropriate.


I think it should be mandatory for people to learn JS before jQuery. Too many answers to JS questions on the web start with "Using jQuery you can just...". I always find it refreshing to see answers that are posed as "you can do it this way...or with jQuery you can..."; it really shows the person's competence with the language when you see they are not exclusively tied to the jQuery framework. Don't get me wrong though, I like jQuery :).
facelessuser
 
Posts: 1575
Joined: Tue Apr 05, 2011 7:38 pm

Re: Sunday JS

Postby agibsonsw on Mon Jun 25, 2012 8:28 pm

@facelessuser Quite agree 8-). Too many do not bother with JS at all. And, as you indicate, too many searches that I make assume jQuery.

Current challenge:
I can navigate all the way backwards to the body, noting the type of element just prior to the body. Assuming this is a, for example, 'p' paragraph, can anyone suggest a simple way to determine which (index) the current paragraph is? That is, what position it is in, in relation to its siblings?

Added: a messy way to do this would be give the para an id, then loop through all the paras checking for this id :o

Added more: I suppose I could give the current parent an id, loop through ALL elements - getElementsByTagName('*') :?: then note the index for this item. Then I wouldn't have to bother navigating around too much/ at all..
"I'm here to save your life. But if I'm going to do that, I'll need total uninanonynymity." Me Myself & Irene.
agibsonsw
 
Posts: 901
Joined: Fri Jan 27, 2012 9:11 pm

Re: Sunday JS

Postby agibsonsw on Mon Jun 25, 2012 9:09 pm

Think I'm just about there :D

When I 'add comment' I'll attach the selected word as a property to the comment-element;
When I 'save comments' I'll also store the selected word, but use the elements-index (within the document) as part of the localStorage key:

Code: Select all
javascript:
(function(){
  var d=document;
  var all=d.getElementsByTagName('*');
  for(var i=0;i<all.length;i++){
    if(all[i].className=='cmt'){
      localStorage['cmt'+i]=all[i].value;
     // also store the attached word
    }
  }
  return false;
}
)();

When I 'append comments' I can extract the index number (from the storage-key) and also use querySelector to find the attached word (within the element):

Code: Select all
javascript:
(function(){
  for(var i=0;i<localStorage.length;i++){
    x=parseInt((localStorage.key(i)).substr(3));
    window.alert(x);
   // locate this xth element and find the attached word within it!
  }
  return false;
}
)();


All I need to do is create the code.. and then discover a reason why I'm doing this :lol:
"I'm here to save your life. But if I'm going to do that, I'll need total uninanonynymity." Me Myself & Irene.
agibsonsw
 
Posts: 901
Joined: Fri Jan 27, 2012 9:11 pm

Re: Sunday JS

Postby agibsonsw on Mon Jun 25, 2012 11:10 pm

Almost there, but not quite - if anyone's willing to assist ;) just for the fun of it ;)

I'm giving the wrapper element the classname 'wrap'. But I need to adjust the stored index-numbers because I've added (temporarily) new elements to the page. Here's the whole code (3 bookmarklets) I have so far:

Code: Select all
javascript:
(function(){
  var d=document;
  var wrap=d.createElement('span');
  wrap.style.position='relative';
  wrap.className='wrap';
  var cmt=d.createElement('textarea');
  cmt.style.backgroundColor='yellow';
  cmt.style.top='1.5em';
  cmt.style.position='absolute';
  cmt.style.zIndex='99';
  cmt.className='cmt';
  cmt.style.height='4em';
  if(window.getSelection){
    var sel=window.getSelection();
    if(sel.rangeCount){
      var rng=sel.getRangeAt(0).cloneRange();
      rng.surroundContents(wrap);
      sel.removeAllRanges();
      sel.addRange(rng);
      wrap.insertBefore(cmt,wrap.firstChild);
      cmt.thetext=rng;
    }
  }
  return false;
}
)();

javascript:
(function(){
  var d=document;
  var all=d.getElementsByTagName('*'),j=0;
  for(var i=0;i<all.length;i++){
    if(all[i].className=='wrap'){
      localStorage['cmt'+(i-j)]=all[i+1].value;
      localStorage['wrd'+(i-j)]=all[i+1].thetext;
     j+=2;
    }
  }
  return false;
}
)();

javascript:
(function(){
  var d=document;
  for(var i=localStorage.length-1;i>=0;i--){
    var skey=localStorage.key(i);
    x=parseInt((skey).substr(3));
    if(skey.substr(0,3)=='cmt'){
     alert(x+' '+localStorage['wrd'+x]+'\n'+localStorage['cmt'+x]);
     found=d.getElementsByTagName('*')[x];
     found.style.color='red';
   }
  }
  return false;
}
)();

The third section isn't quite working as it's not applying red colouring to the correct (parent) element.

When I'm searching to decide where to insert the previously saved comments (the 3rd code section) I'll need to search from the bottom upwards, as I will be inserting new nodes.

I can't use querySelector() to find the previously stored word, because it returns an element. That is, it will return the parent that contains the word - which I already have anyway. I might have to extract the text from the parent and just do a JS string search. It will then be a little messy as I'll have to rebuild the whole text-content and re-insert it.

If I get this working I suppose it may have some uses. You can comment web-pages and, when you revisit the page later, you'll be able to reinstate the comments. I should even be able to reduce it to two (or possibly one) bookmarklet(s).
"I'm here to save your life. But if I'm going to do that, I'll need total uninanonynymity." Me Myself & Irene.
agibsonsw
 
Posts: 901
Joined: Fri Jan 27, 2012 9:11 pm

Next

Return to General Discussion

Who is online

Users browsing this forum: Exabot [Bot] and 16 guests