Monday, October 15, 2012

Handling cross-browser key events with jQuery

Another little copy-paste code fragment from the house of jQuery :) If you need to od different things on keydown event, use this basic structure, so it will be cross-browser friendly:



1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
jQuery(document).keydown(function (e) {

 var evt=(e)?e:(window.event)?window.event:null;
 if(evt){
  var key=(evt.charCode)?evt.charCode:((evt.keyCode)?evt.keyCode:((evt.which)?evt.which:0));
  
  // the key specific code comes here:
  console.log(key);

 }

}); 


I've tested it on Chrome, FF and IE9-10, and it works well.

Wednesday, October 10, 2012

Vertical centering text with CSS

Nowadays I get a lot of designs with vertically centered text in it. Sometimes the text is only a word but it can be multiple lines long too. The problem was, I could not determine, wether the printed text will be broken into multiple lines or not. Luckily, I have found a solution to this problem in pure CSS. This is instantly added to my sitebuilding template for future use.

Here is a little demo showing how it works:
http://cssdesk.com/K36px


Let's see the code:

HTML


1
2
3
4
5
<div class="outer">
   <div class="inner">
      <div class="element">Centered</div>
   </div>
</div>


Default CSS



1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
.outer {
  display: table;
   height: 155px;
   overflow: hidden;
   width: 980px;
}

   .outer .inner {
      display: table-cell;
      vertical-align: middle;
      width: 100%;
      margin: 0 auto;
   }



CSS for IE


1
2
3
.outer { position: relative; }
.outer .inner { position: absolute; top: 50%; }
.outer .inner .element { position: relative; top: -50%; }

Monday, October 8, 2012

Different font size for each font family


No matter how surprising it is, there was no solution to change the font size based on the font family. Until now. :)

What is the problem?

I used two different fonts on a webpage. One is a simple Arial, the other is a custom font. The custom font requires a much bigger font size, to get the same result as the Arial.

What needed to be done?

I needed a solution, to change font size CSS properties on some of the elements, if the custom font is loaded. For example IE could not load it, so there should be Arial everywhere, but with a correct font size.

How is it done?

With Javascript of course. The basic idea is, to create a hidden element with some characters in it. This text has a different with on each font family. So I measured it with Arial, then I have set the font family to the custom value, and measured it again. It the element's width is different than the previous one, then the font is loaded. If the width of the element doesn't changes in 10seconds, the checking interval is cleared, so it doesn't uses the resources anymore. In the callback funcion, I just removed the ".no-custom-font" class from the body, so everything should look good now. Note, that I had to create some other CSS rules (and with the additional class, these rules have a higher specificity than the default ones) to set the right font size values.

Okay, this worked fine in the project, so lets look at the code:



1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
function onWebFontLoaded(font, callback) {
 
 // create a test element for each font
 jQuery("body").append("<div id=\'font_test\' style=\'position: absolute;left:-1000000px; top: -1000000px;font-size:300px;font-family:Arial;font-style:normal;font-weight:normal;letter-spacing:0px;\'>giItT1WQy@!-/#</div>");
 var width = jQuery("#font_test").width();
   
 jQuery("#font_test").css("font-family", font+", Arial");

 var timeWaited = 0;
 var intervalTime = 200; 
 var maxWaitingTime = 10000; // 10 sec
 var interval;
 function checkFont() {
  
  timeWaited += intervalTime;
  
  if(jQuery("#font_test").length > 0 && jQuery("#font_test").width() != width) {
   callback();
   jQuery("#font_test").remove();
   return true;
  }
  else if( timeWaited > maxWaitingTime) {
   clearInterval(interval);
   if( typeof console != "undefined") console.log("font ("+font+") not loaded.");
  }
  return false;
 }

 if(!checkFont()) {
  interval = setInterval(checkFont, intervalTime);
 }
   
};

jQuery(document).ready(function() {
 
 onWebFontLoaded('AGENCYR', function() {
  
  // remove the no-font-... class from the body tag
  jQuery(".no-font-AGENCYR").removeClass("no-font-AGENCYR");
 });
 
});



Wednesday, October 3, 2012

Get single corners rounded with CSS3


There are a lot of posts about making rounded corners with CSS3. But, I haven't found a good, compact one about how to do it only with one corner and as cross-browser friendly as possible.

So, this little post is to remind myself (and others) how to do it. The code is not so short considering what I would like to achieve...there quite a few rows for this task...but, let's talk code:

-moz-border-radius-topleft: 10px;
-webkit-border-top-left-radius: 10px;
-khtml-border-top-left-radius: 10px;
border-top-left-radius: 10px;

I have also added a little macro to my Netbeans install like this:

rclt: (rounded corners left top)
-moz-border-radius-topleft: ${VAR}px;
-webkit-border-top-left-radius: ${VAR}px;
-khtml-border-top-left-radius: ${VAR}px;
border-top-left-radius: ${VAR}px;${cursor}

rcrt: (rounded corners right top)
-moz-border-radius-topright: ${VAR}px;
-webkit-border-top-right-radius: ${VAR}px;
-khtml-border-top-right-radius: ${VAR}px;
border-top-right-radius: ${VAR}px;${cursor}

And so on...

Tuesday, April 17, 2012

Javascript: Count a string occurence in text


While working on my favorite project I had to count the colons in a string. I was wondering which could be the best solution. I had two methods in my mind to solve that.

The first is the "split" method:
var text = 'text text text: text2: text3::';
var colon_length = text.split(":").length - 1;


The other one is the "RegExp" method:
var text = 'text text text: text2: text3::';
var colon_length = text.match(/:/g).length;


After googling for a while I found a test which one is better:
http://jsperf.com/count-the-number-of-occurances-in-string


Well, that graph is self explanatory I guess :) (Thanks jsPerf.com)



In the mean time I stumbled into this solution which I think is quite interesting:
http://www.codecodex.com/wiki/index.php?title=Count_the_number_of_occurrences_of_a_specific_character_in_a_string#JavaScript

This is the code:
String.prototype.count=function(s1) { 
 return (this.length - this.replace(new RegExp(s1,"g"), '').length) / s1.length;
}


And this is the usage:
// use like this 
test = "this, as you see, is a sentence, containing many ','s";
numberOfCommas = test.count(','); //4
// or 
numberOfSblank = test.count('s '); //2


But because in my project I have to use a minimal amount of global footstep I sticked to the "RegExp" method.

Wednesday, April 11, 2012

Copy jQuery click event from one element to another



Today I had to solve a task wich seemed to be quite easy... I had to copy a "click" event from an element. To copy an "onclick" attribute...it's a piece of cake. BUT, how do you copy a jQuery "click" event?

I found an interesting conversation about this problem here, but I couldn't find a solution for my problem:
http://forum.jquery.com/topic/how-do-i-copy-the-click-event-from-one-element-to-another.

I'm not 100% sure you understand my problem, so here are some example codes:


This is a common "a" tag with an onclick attribute:

<a href="javascript:void(null)" onclick="alert('onclick event triggered')" id="test_element">Trigger OnClick</a>

This click event can be copied with this code:

jQuery("#test_element").attr("onclick")




BUT, what's the case with this method?

<a href="javascript:void(null)" id="test_element">Trigger OnClick</a>

And with this Javascript:

jQuery("#test_element").click(function() {
    alert("jQuery click event triggered");
})




How do I copy this click event?

Well, I dove in jQuery-s source code because I knew it can be done. jQuery has a .clone() function which has an attribute to decide, whether to copy the events too or not. This is what I found:

function cloneCopyEvent( src, dest ) {

 if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) {
  return;
 }

 var type, i, l,
  oldData = jQuery._data( src ),
  curData = jQuery._data( dest, oldData ),
  events = oldData.events;

 if ( events ) {
  delete curData.handle;
  curData.events = {};

  for ( type in events ) {
   for ( i = 0, l = events[ type ].length; i < l; i++ ) {
    jQuery.event.add( dest, type, events[ type ][ i ] );
   }
  }
 }

 // make the cloned public data object a copy from the original
 if ( curData.data ) {
  curData.data = jQuery.extend( {}, curData.data );
 }
}


The most interesting part is this:

oldData = jQuery._data( src ),
curData = jQuery._data( dest, oldData ),
events = oldData.events;


The events are saved inside (jQuery._data( src )).events. To stay by my example, the solution will look like this:

(jQuery._data( jQuery("#test_element")[0] )).events.click[0].handler


This way I can make a copy of the function bound to the element. And of course, if there are more bindings to one element, they can be reached with "...click[1].handler", "...click[2].handler", etc.


I hope this little trick helped you too.





Friday, March 23, 2012

jQuery simple banner rotator plugin

Today I had a task, to create a simple image (banner) rotator in Javascript...so I thought: If I have to do it, then do it right and created a simple jQuery plugin. The code is really easy to understand... here is the essence of it:


setInterval(function () {
 var items = jQuery( jQuery(options.items, this).get().reverse());

 items.each(function () {
  // if visible, than hide
  if (jQuery(this).is(":visible")) {
   jQuery(this).fadeOut();

   return false;
  } 
 });

 // if nothing more to hide, show all
 if( jQuery(":visible", items).length <= 1 ) items.fadeIn();

}, options.timeout);


Basically there is an interval and it's hiding all the items backwards. When they are all hidden, then show them. Because the items are positionated "absolute" they are overlapping eachother. So when I hide one, another appears.


That's it. Simple and small! :)



Here is the full code:
jQuery.bannerRotate.js

Cheers,
Phil