Webkit transitionEnd event grouping - javascript

I have a HTML element to which I have attached a webkitTransitionEnd event.
function transEnd(event) {
alert( "Finished transition!" );
}
var node = document.getElementById('node');
node.addEventListener( 'webkitTransitionEnd', transEnd, false );
Then I proceed to change its CSS left and top properties like:
node.style.left = '400px';
node.style.top = '400px';
This causes the DIV to move smoothly to the new position. But, when it finishes, 2 alert boxes show up, while I was expecting just one at the end of the animation. When I changed just the CSS left property, I get one alert box - so this means that the two changes to the style are being registered as two separate events. I want to specify them as one event, how do I do that?
I can't use a CSS class to apply both the styles at the same time because the left and top CSS properties are variables which I will only know at run time.

Check the propertyName event:
function transEnd(event) {
if (event.propertyName === "left") {
alert( "Finished transition!" );
}
}
var node = document.getElementById('node');
node.addEventListener( 'webkitTransitionEnd', transEnd, false );
That way, it will only fire when the "left" property is finished. This would probably work best if both properties are set to the same duration and delay. Also, this will work if you change only "left", or both, but not if you change only "top".
Alternatively, you could use some timer trickery:
var transEnd = function anon(event) {
if (!anon.delay) {
anon.delay = true;
clearTimeout(anon.timer);
anon.timer = setTimeout(function () {
anon.delay = false;
}, 100);
alert( "Finished transition!" );
}
};
var node = document.getElementById('node');
node.addEventListener( 'webkitTransitionEnd', transEnd, false );
This should ensure that your code will run at most 1 time every 100ms. You can change the setTimeout delay to suit your needs.

just remove the event:
var transEnd = function(event) {
event.target.removeEventListener("webkitTransitionEnd",transEnd);
};
it will fire for the first property and not for the others.

If you prefer it in JQuery, try this out.
Note there is an event param to store the event object and use within the corresponding function.
$("#divId").bind('oTransitionEnd transitionEnd webkitTransitionEnd', event, function() {
alert(event.propertyName)
});

from my point of view the expected behaviour of the code would be to
trigger an alert only when the last transition has completed
support transitions on any property
support 1, 2, many transitions seamlessly
Lately I've been working on something similar for a page transition manager driven by CSS timings.
This is the idea
// Returs the computed value of a CSS property on a DOM element
// el: DOM element
// styleName: CSS property name
function getStyleValue(el, styleName) {
// Not cross browser!
return window.getComputedStyle(el, null).getPropertyValue(styleName);
}
// The DOM element
var el = document.getElementById('el');
// Applies the transition
el.className = 'transition';
// Retrieves the number of transitions applied to the element
var transitionProperties = getStyleValue(el, '-webkit-transition-property');
var transitionCount = transitionProperties.split(',').length;
// Listener for the transitionEnd event
function eventListener(e) {
if (--transitionCount === 0) {
alert('Transition ended!');
el.removeEventListener('webkitTransitionEnd', eventListener);
}
}
el.addEventListener('webkitTransitionEnd', eventListener, false);
You can test here this implementation or the (easier) jQuery version, both working on Webkit only

If you are using webkit I assume you are mobilizing a web-application for cross platform access.
If so have you considered abstracting the cross platform access at the web-app presentation layer ?
Webkit does not provide native look-and-feel on mobile devices but this is where a new technology can help.

Related

2nd function in toggle function not working

I want to toggle the width of an element on click of another.
I've used the script someone has made here: http://jsbin.com/ohOZEYI/1/edit?html,css,js,output
and just replaced it with my own elements:
function a(el){
$(el).animate({width: "10%"}, 1500);
}
function b(el){
$(el).animate({width: "100%"}, 1500);
}
$("#mybtn").click(function() {
var el = $('#container');
return (el.t = !el.t) ? a(el) : b(el);
});
The first function (a) works fine and shrinks the element to 10% width. But the 2nd function doesnt work.
Would anyone know why?
The jQuery function doesn't return DOM nodes. It returns an object that contains a collection of DOM nodes.
var el = $('#container');
creates a new instance of an object which happens to contain a reference to the DOM node with an ID of container. Every time the function gets called, you get a brand new instance of el, which means your toggle can never toggle.
To fix this, you're going to want to store the toggle data on the DOM node.
Use the .data() method:
var t = el.data('toggle');
if (t) {
a(el);
} else {
b(el);
}
el.data('toggle', !t);

Javascript events on top-of-window enter and leave?

I'm seeking a mouse event to detect when the mouse enter the top of the window, and leaves the top of the window. I don't mean the top of the webpage, but the top of the window.
There's no pre-existing "element" on the page i'm trying to attach the event to, but i think programmatically adding an invisible, fixed html element to the top of the page might be ok.
I like the clientY method with onmousemove, but that will fire repeatedly, which i don't want-- only want firing on enter and leave. Don't want my code to have to handle multiple firings.
This should work with ANY webpage-- i do not have any control over the HTML on the page (except for elements i add to the page programmatically).
Need only support modern browsers, simplest method possible, no jquery.
This method works great! But it prevents clicking elements behind it, which is not ok.
(function (){
var oBanana = document.createElement("div");
oBanana.style.position = "fixed"
oBanana.style.top = "0"
oBanana.style.left = "0"
oBanana.style.height= "100px"
oBanana.style.width = "100%"
oBanana.addEventListener("mouseover", function(event) {alert('in');});
oBanana.addEventListener("mouseout", function(event) {alert('out');});
document.body.appendChild(oBanana);
})();
Next i tried this, which inserts a small hotzone at the top of the page. I realized that, due to my scenario, i DON'T want mouse-out on the hotzone-- rather i want mouseover on everything BELOW the hotzone. Here's my first attempt at that, but fails because the hotzone gets the body event, plus the body event fires repeatedly:
(function (){
var oHotzone = document.createElement("div");
oHotzone.id = "fullscreen-hotzone"
oHotzone.style.position = "fixed"
oHotzone.style.top = "0"
oHotzone.style.left = "0"
oHotzone.style.height= "10px"
oHotzone.style.width = "100%"
oHotzone.addEventListener("mouseover", function(event) {alert('hotzone');});
document.body.appendChild(oHotzone);
document.body.style.marginTop = "10px"
document.body.addEventListener("mouseover", function(event) {alert('body');});
})();
Appreciate any help!
Thx!
It's the simplest you can do with vanilla javascript.
Function:
// create a one-time event
function onetime(node, type, callback) {
// create event
node.addEventListener(type, function(e) {
// remove event
e.target.removeEventListener(e.type, arguments.callee);
// call handler
return callback(e);
});
}
Implementation:
// one-time event
onetime(document.getElementById("hiddenTopElement"), "mouseenter", handler);
onetime(document.getElementById("hiddenTopElement"), "mouseleave" , hanlder);
// handler function
function handler(e) {
alert("You'll only see this once!");
}
my OP could have probably been stated better, but i'm happy with this solution. The fixed div blocked hover events on the body below, so the body hover event does not happen until the mouse leaves the hotzone. Perfect.
// create hotzone and add event
var oHotzone = document.createElement("div");
oHotzone.id = "fullscreen-hotzone"
oHotzone.style.position = "fixed"
oHotzone.style.top = "0"
oHotzone.style.left = "0"
oHotzone.style.height= "10px"
oHotzone.style.width = "100%"
oHotzone.addEventListener("mouseenter", function(event) {alert('hotzone');});
document.body.appendChild(oHotzone);
document.body.addEventListener("mouseenter", function(event) {alert('body');});

Touch events in JavaScript perform two different actions

Since I experienced a very strange issue with different touch event libraries (like hammer.js and quo.js) I decided to develop the events I need on my own. The Issue I'm talking about is that a touch is recognized twice if a hyperlink appears on the spot where I touched the screen. This happens on iOS as well as Android.
Imagine an element on a web page that visually changes the content of the screen if you touch it. And if the new page shows a hyperlink (<a href="">) at the same spot where I touched the screen before that new hyperlink gets triggered as well.
Now I developed my own implementation and I noticed: I'm having the same problem! Now is the question: Why?
What I do is the following (yes, I'm using jQuery):
(see source code below #1)
This function is only used with some special elements, not hyperlinks. So hyperlinks still have the default behavior.
The problem only affects hyperlinks. It doesn't occur on other elements that use the event methods showed above.
So I can imagine that not a click event is fired on the same element I touch but a click 'action' is performed at the same spot where I touched the screen after the touch event was processed. At least this is what it feels like. And since I only catch the click event on the element I actually touch I don't catch the click event on the hyperlink - and actually that shouldn't be necessary.
Does anyone know what causes this behavior?
Full source codes
#1 - attatch event to elements
$elements is a jQuery object returned by $( selector );
callback is the function that should be called if a tap is detected
helper.registerTapEvent = function($elements, callback) {
var touchInfo = {
maxTouches: 0
};
function evaluate(oe) {
var isSingleTouch = touchInfo.maxTouches === 1,
positionDifferenceX = Math.abs(touchInfo.startX - touchInfo.endX),
positionDifferenceY = Math.abs(touchInfo.startY - touchInfo.endY),
isAlmostSamePosition = positionDifferenceX < 15 && positionDifferenceY < 15,
timeDifference = touchInfo.endTime - touchInfo.startTime,
isShortTap = timeDifference < 350;
if (isSingleTouch && isAlmostSamePosition && isShortTap) {
if (typeof callback === 'function') callback(oe);
}
}
$elements
.on('touchstart', function(e) {
var oe = e.originalEvent;
touchInfo.startTime = oe.timeStamp;
touchInfo.startX = oe.changedTouches.item(0).clientX;
touchInfo.startY = oe.changedTouches.item(0).clientY;
touchInfo.maxTouches = oe.changedTouches.length > touchInfo.maxTouches ? oe.touches.length : touchInfo.maxTouches;
})
.on('touchend', function(e) {
var oe = e.originalEvent;
oe.preventDefault();
touchInfo.endTime = oe.timeStamp;
touchInfo.endX = oe.changedTouches.item(0).clientX;
touchInfo.endY = oe.changedTouches.item(0).clientY;
if (oe.touches.length === 0) {
evaluate(oe);
}
})
.on('click', function(e) {
var oe = e.originalEvent;
oe.preventDefault();
});
}
#2 - the part how the page transition is done
$subPageElem is a jQuery object of the page that should be displayed
$subPageElem.prev() returns the element of the current page that hides (temporarily) when a new page shows up.
$subPageElem.css({
webkitTransform: 'translate3d(0,0,0)',
transform: 'translate3d(0,0,0)'
}).prev().css({
webkitTransform: 'translate3d(-5%,0,-100px)',
transform: 'translate3d(-5%,0,-100px)'
});
I should also mention that the new page $subPageElem is generated dynamically and inserted into the DOM. I. e. that link that gets triggered (but shouldn't) doesn't even exist in the DOM when I touch/release the screen.

jQuery custom plugin - setting private options for multiple instances

Hi folks!
I'm currently developing a client project where I saw myself doing the same javascript code over and over again. So I though it would be useful to wrap the logic inside a custom jQuery plugin. I've achieved it for a single instance of the plugin, but for multiple instances, I think I'm having a problem with the properties of each instance overwriting each other.
Well, let's get to the code! Here is the currently code that I have for the plugin:
// RESPONSIVE MENU ===========================//
// wrapper for a responsive menu plugin, //
// made by Favolla Comunicação //
//============================================//
/* INSTRUCTIONS
Apply the plugin on the main wrapper of the responsive menu. For example:
$(#menu).responsiveMenu($(#trigger));
The plugin just toggles the classes, leaving the effects and layout for the css
CONFIG
- trigger: the selector of the button that will activate the menu (required)
- activeClass: class name to be injectet when the toggle is activated (default: active)
- submenuTrigger: the selector of the buttons that will activate the submenus, if the menu will have another levels (default: $('sub-toggle'))
- submenu: the selector of the submenus (default: $('.submenu'))
- submenuActiveClass: class name to be injected on the submenus when they are activated (default: open)
- breakpoint: max window whidth where the plugin will work (default: 720)
- timeOut: time in milissegundos to limite the onResize repeat. (default: 100)
- moveCanvas: option to activate the "off canvas" pattern or not (just puts a class on the main elements of the page). (default: false)
- canvas: class name of the elements that build the "canvas" (default: null)
*/
;(function ( $, window, document, undefined ) {
$.fn.responsiveMenu = function(settings){
var config = {
'trigger': '',
'activeClass': 'active',
'submenuTrigger': $('.sub-toggle'),
'submenu': false,
'submenuActiveClass': 'open',
'breakpoint': 720,
'timeOut': 100,
'moveCanvas': false,
'canvas': '',
};
if (settings){$.extend(config, settings);}
// plugin variables
var mTrigger,
menu = $(this),
active = config.activeClass,
button = config.trigger,
bpoint = config.breakpoint,
submTrigger = config.submenuTrigger,
submenu = config.submenu,
submenuActive = config.submenuActiveClass;
canvasOn = config.moveCanvas;
canvas = config.canvas;
time = config.timeOut;
return this.each(function () {
if($(window).width() > bpoint){
mTrigger = false;
} else {
mTrigger = true;
}
onChange = function(){
clearTimeout(resizeTimer);
var resizeTimer = setTimeout(function(){
if($(window).width() > bpoint){
mTrigger = false;
menu.removeClass(active);
button.removeClass(active);
if(canvasOn){
canvas.removeClass(active);
}
} else {
mTrigger = true;
}
}, time);
}
$(window).bind('resize',onChange);
$(document).ready(onChange);
button.click(function(){
if(mTrigger) {
menu.toggleClass(active);
button.toggleClass(active);
if(canvasOn){
canvas.toggleClass(active);
}
}
});
if(submenu){
var submenuClass = '.' + submenu.prop('class');
// toggle for the submenus
submTrigger.click(function(){
if(mTrigger) {
if($(this).hasClass(active)){
submTrigger.removeClass(active);
submenu.removeClass(submenuActive);
} else {
submTrigger.removeClass(active);
$(this).addClass(active);
submenu.removeClass(submenuActive);
$(this).next(submenuClass).addClass(submenuActive);
}
}
});
}
});
}
})( jQuery, window, document );
And then, when I want to apply the plugin, I make like this:
$('#menu-wrapper').responsiveMenu({
trigger: $('#nav-toggle'),
submenu: $('.submenu'),
submenuTrigger: $('.submenu-toggle'),
moveCanvas: true,
canvas: $('.canvas'),
breakpoint: 862
});
$('#search').responsiveMenu({
trigger: $('#search-toggle'),
breakpoint: 862
});
The main issue here is when I set to instances of the responsiveMenu();, it seems like some options are overwriting. For example, the first instance set moveCanvas to true, and it works, but when I leave it blank for the second instance (which leaves the moveCanvas option set to false for this element, this options for the first instance don't work anymore.
I know that maybe I'm not following the jQuery plugin best pratices, and I even read something about the jQuery Boilerplate, which looks great, but I'm not an advanced javascript developer, so there a lot of things that I could do better, but I just don't now how to do.
Anyway, any help with this issue (and opinions about the plugin) will be very welcome!
var mTrigger,
menu = $(this),
active = config.activeClass,
button = config.trigger,
bpoint = config.breakpoint,
submTrigger = config.submenuTrigger,
submenu = config.submenu,
Until here you were doing correct.
submenuActive = config.submenuActiveClass;
canvasOn = config.moveCanvas;
canvas = config.canvas;
time = config.timeOut;
Then, you introduced a semicolon - which leads to the further assignments (canvasOn, canvas and time) not being part of the var statement any more. They're no variable declarations, you assign to global variables here - and that way you overwrite the settings of the first plugin instance.
Change every but the last semicolon to commata.
You need to encapsulate your settings in a class or closure and store them for each element the plugin is called on.
return this.each(function () {
...
$(this).data('some-key', settings);
...
});
If you'd like to learn about jQuery plugin authoring, they have an article on it here http://learn.jquery.com/plugins/basic-plugin-creation/

combining a method to hide or show content

I was wondering how I can combine a hide function and show function into 1 toggle Function that either fades in content or fades it out, im guessing this argument would update the fade method:
This is my current effort of JS using jQuery from my object but is totally wrong:
toggleAlertOverlay: function (state) {
var instance = this;
if (state === hide) {
instance.selector.fadeOut();
}
elseif(state === show) {
instance.selector.fadeIn();
}
},
toggleAlertOverlay(hide);
Try using .fadeToggle()
The .fadeToggle() method animates the opacity of the matched elements. When called on a visible element, the element's display style property is set to none once the opacity reaches 0, so the element no longer affects the layout of the page.
$(<element>).fadeToggle();
Where <element> is a valid selector ....
var toggleState = 'none';
toggleAllertOverlay: function()
{
if(toggleState == 'none')
{
$('#element').fadeIn();
toggleState == 'showing';
}
else
{
$('#element').fadeOut();
toggleState == 'none';
}
}
is just a concept based on what you have. Note the variable outside of the function. Its outside so it doesn't get destroyed upon function complete. But aside from that, theres various other methods you can try if this doesn't suit you. Up to and including jQuery toggle()

Categories

Resources