Rubberband with iScroll and jQuery mobile - javascript

The headline says it all. At start of my app I retrieve data from a php file (some divs) and append them to an wrapper-div. Around this wrapper-div (not called wrapper) is the iScroll wrapper.
iScroll is working, but there is a rubberband effect.
Here's the (index) HTML:
<div data-role="header" data-theme="c" data-position="fixed">
<h1>Title</h1>
</div><!-- /header -->
<div id="wrapper">
<div id="scroller">
<div data-role="content" id="content">
<div id="headlinesindex">
<div class="span3" id="9999999999"></div>
</div>
</div>
</div>
</div>
<script>
$(document).ready(function() {
onBodyLoad();
});
</script>
And here's the javascript-file:
function onBodyLoad()
{
$.ajax({
url: "headlines_getter.php?last="+ $(".span3:last").attr('id') ,
success: function(html) {
if(html){
$("#headlinesindex").append(html);
setTimeout(function () {
myScroll.refresh();
}, 0);
}
}
});
}
function onDeviceReady()
{
var myScroll = new iScroll('wrapper');
}
I've played arround with the setTimeout as it is explained at iscroll.com, but it changes nothing... Hope you know what's wrong.
Thanks in advance. Best regards, John.

I had the same issue.
It came from the outer "wrapper" not being sized correctly in iscroll.
If it is sized the same size as the inner "scroller" height then the iscroll will have no where to go and rubber band.
I fixed it for me, and created a fork for others with the same issue:
https://github.com/meckdahl/iscroll
================================== Advanced Usage
Here is some addon functions I use to maintain my 20+ scroll containers in our Spine.JS mobile app:
For each page I set a specific wrapper like such:
<div id="wrapper2">
Then I dynamically create iScroll only if that page is loaded:
After the content for the page is loaded I call like such:
window.resetScroll(2)
window.setScrolling(true)
This will re-initialize iScroll for this page.
Here are the functions I define on my root page:
<script type="text/javascript">
// maximum wrapper index = 23 currently (9/12/12)
var myScrolls = [];
myScrolls.length = 29; // Scrolls to look for wrapper1-30
var refreshScrolling = function() {
//console.log('refreshScrolling Active Scroll Items: ');
myScrolls.forEach( function(scrollItem){
scrollItem.refresh();
});
};
var refreshScroll = function(wrapperNumber) {
//console.log('refreshScroll wrapperNumber: wrapper' + wrapperNumber.toString());
var i = wrapperNumber;
setTimeout(function () {
(myScrolls[i-1]).refresh();
}, 100);
};
// This looks for and initializes and dynamic scrolls that Spine recently put in memory
// and have not been initialized yet.
var setScrolling = function() {
for (var i=1; i < myScrolls.length+1; i++){
if (($("#wrapper"+(i).toString()).length !== 0) ){
if((myScrolls[i-1] !== null) && (myScrolls[i-1] !== undefined)){
// Already setup
}
else{
myScrolls[i-1] = new iScroll('wrapper'+ (i).toString(),
{ hScroll: false, hScrollbar: false, vScrollbar: false });
created.");
}
}
}
}
// This must be called on a view with dynamic content to re-create the view to fit the potentially
// changing content size. It will only rebuild the one scroll whose index is passed in.
// The index should be the wrapper# for the view attached to the controller.
// Call setScrolling after this to catch any uninitialized views.
var resetScroll = function(wrapperNumber) {
var i = wrapperNumber;
// if (!(i in myScrolls)) continue; // skip nonexistent elements && !(myScrolls[i-1] )
if (($("#wrapper"+(i).toString()).length !== 0) ){
if( (myScrolls[i-1] !== null) && (myScrolls[i-1] !== undefined)){
// Destroy Skipped right now
myScrolls[i-1].destroy();
myScrolls[i-1] = null;
}
myScrolls[i-1] = new iScroll('wrapper'+ (i).toString(),
{ hScroll: false, hScrollbar: false, vScrollbar: false });
created.");
}
}
function loaded() {
setTimeout(function () {
setScrolling();
}, 100);
}
window.addEventListener('load', loaded, false);
</script>

I had the same problem with my custom script so I changed the code and now it's working nicely:
var myScroll;
function loaded() {
setTimeout(function(){
myScroll = new iScroll('wrapper');
myScroll.refresh();
} , 100 );
}
And I call it on "onDeviceReady":
function onDeviceReady()
{
loaded();
}

http://jsfiddle.net/Eccgy/
Check this may be help you

Here is a simple iscroller that would help .
its very easy to implement
include scripts and jsut add an attribute data-iscroll to the div, which you need the effect.
https://github.com/watusi/jquery-mobile-iscrollview

Related

Use a parameter value in a callback function

I'm having issues getting a 'touchmove' event to fire using a parameter value in the callback function. I have success if I pass an "alert" but not a function. Basic concept in the example is the text box goes up by "1" (obj.number) on the 'touchmove' when it's scrolled within the selected div (ID="myDIV").
Here is an example:
function myFunction(num) {
document.getElementById("divText").innerHTML = x += num;
}
function Scroller(area, obj) {
$divarea = document.getElementById(area);
$divarea.addEventListener("touchstart", function() {
obj.alertMe(obj.number); // THIS WORKS
});
$divarea.addEventListener("touchmove", function() {
obj.scrollMe(obj.number) // THIS DOES NOT WORK AND WHAT I NEED TO RUN, "obj.number" SHOULD BE 1
});
var x = 0;
}
$scroll = new Scroller("myDIV", {
number: 1,
alertMe: function(numberValue) {
alert(numberValue)
},
scrollMe: function(numberValue) {
myFunction(numberValue)
}
})
The body is just basic HTML:
<div id="myDIV" >
<!-- a bunch of text goes here so that it scrolls -->
</div>
<div id="divText" ></div>
Would greatly appreciate the overflow community in helping me spot my bug as I have put too much time already into what I know (hope) is going to be a simple solution.
Thanks!
Initialize x as a global variable and that will solve your issue
function Scroller(area, obj) {
$divarea = document.getElementById(area);
$divarea.addEventListener("touchstart", function() {
obj.alertMe(obj.number); // THIS WORKS
});
$divarea.addEventListener("touchmove", function() {
obj.scrollMe(obj.number) // THIS DOES NOT WORK AND WHAT I NEED TO RUN, "obj.number" SHOULD BE 1
});
x = 0;
}

0x8000ffff JavaScript runtime error Unexpected call to method or property access

Every time when I try to run this code in visual studio I get the above error. I am not sure what is going on. I know it's question like this, but I did not see this error in the answered in the solutions to this problem. Can anyone help me with this?
// display the lightbox
function lightbox() {
var insertContent = "<div id='chartCon'>Test</div>";
// jQuery wrapper (optional, for compatibility only)
(function ($) {
// add lightbox/shadow <div/>'s if not previously added
if ($('#lightbox').size() == 0) {
var theLightbox = $('<div id="lightbox" class="highcharts-container"/>');
var theShadow = $('<div id="lightbox-shadow"/>');
$(theShadow).click(function (e) {
closeLightbox();
});
$('body').append(theShadow);
$('body').append(theLightbox);
//$().keydown(function (e) {
// if (e.which == 27) {
// closeLightbox();
// }
//});
}
// remove any previously added content
$('#lightbox').empty();
// insert HTML content
if (insertContent != null) {
$('#lightbox').append(insertContent);
}
//create chart
var chart = $('#ChartContainer').highcharts('StockChart');
var annots = chart.exportData()
window.chartwidth = $('#ChartContainer').width();
//chart.destroy();
window.chartops.series = window.seriesOptions;
$('#lightbox').highcharts('StockChart', window.chartops);
$('#lightbox').highcharts('StockChart').importData(annots);
// move the lightbox to the current window top
$('#lightbox').css('top', $(window).scrollTop());
// display the lightbox
$('#lightbox').show();
$('#lightbox-shadow').show();
})(jQuery); // end jQuery wrapper
}
// close the lightbox
function closeLightbox() {
// jQuery wrapper (optional, for compatibility only)
(function ($) {
//export possibly changed annotations and reset chartwidth
var chart = $('#lightbox').highcharts('StockChart');
var annots = chart.exportData()
$('#ChartContainer').highcharts('StockChart').removeAllAnnotations();
$('#ChartContainer').highcharts('StockChart').importData(annots);
window.chartwidth = $('#ChartContainer').width();
// hide lightbox/shadow <div/>'s
$('#lightbox').hide();
$('#lightbox-shadow').hide();
// remove contents of lightbox in case a video or other content is actively playing
$('#lightbox').empty();
})(jQuery); // end jQuery wrapper
}
Sorry, this is the function giving me the error
init: function (chart) {
var rangeSelector = this,
options = chart.options.rangeSelector,
buttonOptions = options.buttons || [].concat(rangeSelector.defaultButtons),
selectedOption = options.selected,
blurInputs = rangeSelector.blurInputs = function () {
var minInput = rangeSelector.minInput,
maxInput = rangeSelector.maxInput;
if (minInput) {
minInput.blur();
}
if (maxInput) {
maxInput.blur();
}
};

AngularJS : How to run JavaScript from inside Directive after directive is compiled and linked

I have a responsive template that I am trying to use with my Angularjs app. This is also my first Angular app so I know I have many mistakes and re-factoring in my future.
I have read enough about angular that I know DOM manipulations are suppose to go inside a directive.
I have a javascript object responsible for template re-sizes the side menu and basically the outer shell of the template. I moved all of this code into a directive and named it responsive-theme.
First I added all the methods that are being used and then I defined the App object at the bottom. I removed the function bodies to shorten the code.
Basically the object at the bottom is a helper object to use with all the methods.
var directive = angular.module('bac.directive-manager');
directive.directive('responsiveTheme', function() {
return {
restrict: "A",
link: function($scope, element, attrs) {
// IE mode
var isRTL = false;
var isIE8 = false;
var isIE9 = false;
var isIE10 = false;
var sidebarWidth = 225;
var sidebarCollapsedWidth = 35;
var responsiveHandlers = [];
// theme layout color set
var layoutColorCodes = {
};
// last popep popover
var lastPopedPopover;
var handleInit = function() {
};
var handleDesktopTabletContents = function () {
};
var handleSidebarState = function () {
};
var runResponsiveHandlers = function () {
};
var handleResponsive = function () {
};
var handleResponsiveOnInit = function () {
};
var handleResponsiveOnResize = function () {
};
var handleSidebarAndContentHeight = function () {
};
var handleSidebarMenu = function () {
};
var _calculateFixedSidebarViewportHeight = function () {
};
var handleFixedSidebar = function () {
};
var handleFixedSidebarHoverable = function () {
};
var handleSidebarToggler = function () {
};
var handleHorizontalMenu = function () {
};
var handleGoTop = function () {
};
var handlePortletTools = function () {
};
var handleUniform = function () {
};
var handleAccordions = function () {
};
var handleTabs = function () {
};
var handleScrollers = function () {
};
var handleTooltips = function () {
};
var handleDropdowns = function () {
};
var handleModal = function () {
};
var handlePopovers = function () {
};
var handleChoosenSelect = function () {
};
var handleFancybox = function () {
};
var handleTheme = function () {
};
var handleFixInputPlaceholderForIE = function () {
};
var handleFullScreenMode = function() {
};
$scope.App = {
//main function to initiate template pages
init: function () {
//IMPORTANT!!!: Do not modify the core handlers call order.
//core handlers
handleInit();
handleResponsiveOnResize(); // set and handle responsive
handleUniform();
handleScrollers(); // handles slim scrolling contents
handleResponsiveOnInit(); // handler responsive elements on page load
//layout handlers
handleFixedSidebar(); // handles fixed sidebar menu
handleFixedSidebarHoverable(); // handles fixed sidebar on hover effect
handleSidebarMenu(); // handles main menu
handleHorizontalMenu(); // handles horizontal menu
handleSidebarToggler(); // handles sidebar hide/show
handleFixInputPlaceholderForIE(); // fixes/enables html5 placeholder attribute for IE9, IE8
handleGoTop(); //handles scroll to top functionality in the footer
handleTheme(); // handles style customer tool
//ui component handlers
handlePortletTools(); // handles portlet action bar functionality(refresh, configure, toggle, remove)
handleDropdowns(); // handle dropdowns
handleTabs(); // handle tabs
handleTooltips(); // handle bootstrap tooltips
handlePopovers(); // handles bootstrap popovers
handleAccordions(); //handles accordions
handleChoosenSelect(); // handles bootstrap chosen dropdowns
handleModal();
$scope.App.addResponsiveHandler(handleChoosenSelect); // reinitiate chosen dropdown on main content resize. disable this line if you don't really use chosen dropdowns.
handleFullScreenMode(); // handles full screen
},
fixContentHeight: function () {
handleSidebarAndContentHeight();
},
setLastPopedPopover: function (el) {
lastPopedPopover = el;
},
addResponsiveHandler: function (func) {
responsiveHandlers.push(func);
},
// useful function to make equal height for contacts stand side by side
setEqualHeight: function (els) {
var tallestEl = 0;
els = jQuery(els);
els.each(function () {
var currentHeight = $(this).height();
if (currentHeight > tallestEl) {
tallestColumn = currentHeight;
}
});
els.height(tallestEl);
},
// wrapper function to scroll to an element
scrollTo: function (el, offeset) {
pos = el ? el.offset().top : 0;
jQuery('html,body').animate({
scrollTop: pos + (offeset ? offeset : 0)
}, 'slow');
},
scrollTop: function () {
App.scrollTo();
},
// wrapper function to block element(indicate loading)
blockUI: function (ele, centerY) {
var el = jQuery(ele);
el.block({
message: '<img src="./assets/img/ajax-loading.gif" align="">',
centerY: centerY !== undefined ? centerY : true,
css: {
top: '10%',
border: 'none',
padding: '2px',
backgroundColor: 'none'
},
overlayCSS: {
backgroundColor: '#000',
opacity: 0.05,
cursor: 'wait'
}
});
},
// wrapper function to un-block element(finish loading)
unblockUI: function (el) {
jQuery(el).unblock({
onUnblock: function () {
jQuery(el).removeAttr("style");
}
});
},
// initializes uniform elements
initUniform: function (els) {
if (els) {
jQuery(els).each(function () {
if ($(this).parents(".checker").size() === 0) {
$(this).show();
$(this).uniform();
}
});
} else {
handleUniform();
}
},
updateUniform : function(els) {
$.uniform.update(els);
},
// initializes choosen dropdowns
initChosenSelect: function (els) {
$(els).chosen({
allow_single_deselect: true
});
},
initFancybox: function () {
handleFancybox();
},
getActualVal: function (ele) {
var el = jQuery(ele);
if (el.val() === el.attr("placeholder")) {
return "";
}
return el.val();
},
getURLParameter: function (paramName) {
var searchString = window.location.search.substring(1),
i, val, params = searchString.split("&");
for (i = 0; i < params.length; i++) {
val = params[i].split("=");
if (val[0] == paramName) {
return unescape(val[1]);
}
}
return null;
},
// check for device touch support
isTouchDevice: function () {
try {
document.createEvent("TouchEvent");
return true;
} catch (e) {
return false;
}
},
isIE8: function () {
return isIE8;
},
isRTL: function () {
return isRTL;
},
getLayoutColorCode: function (name) {
if (layoutColorCodes[name]) {
return layoutColorCodes[name];
} else {
return '';
}
}
};
}
};
});
Originally the App.init() object method would be called at the bottom of any regular html page, and I have others that do certain things also that would be used on specific pages like Login.init() for the login page and so forth.
I did read that stackoverflow post
"Thinking in AngularJS" if I have a jQuery background? and realize that I am trying to go backwards in a sense, but I want to use this template that I have so I need to retro fit this solution.
I am trying to use this directive on my body tag.
<body ui-view="dashboard-shell" responsive-theme>
<div class="page-container">
<div class="page-sidebar nav-collapse collapse" ng-controller="SidemenuController">
<sidemenu></sidemenu>
</div>
<div class="page-content" ui-view="dashboard">
</div>
</div>
</body>
So here is my problem. This kinda sorta works. I don't get any console errors but when I try to use my side menu which the javascript for it is in the directive it doesn't work until I go inside the console and type App.init(). After that all of the template javascript works. I want to know how to do responsive theme stuff in these directives. I have tried using it both in the compile and link sections. I have tried putting the code in compile and link and calling the $scope.App.init() from a controller and also at the bottom after defining everything. I also tried putting this in jsfiddle but can't show a true example without having the console to call App.init().
My end design would be having some way to switch the pages through ui-router and when a route gets switched it calls the appropriate methods or re-runs the directive or something. The only method that will run on every page is the App.init() method and everything else is really page specific. And technically since this is a single page app the App.init() only needs to run once for the application. I have it tied to a parent template inside ui-router and the pages that will switch all use this shell template. There are some objects that need to access other to call their methods.
Im sorry in advance for maybe a confusing post. I am struggling right now trying to put together some of the ways that you do things from an angular perspective. I will continue to edit the post as I get responses to give further examples.
You said I have read enough about angular that I know DOM manipulations are suppose to go inside a directive but it sounds like you missed the point of a directive. A directive should handle DOM manipulation, yes, but not one directive for the entire page. Each element (or segment) of the page should have its own directive (assuming DOM manip needs to be done on that element) and then the $controller should handle the interactions between those elements and your data (or model).
You've created one gigantic directive and are trying to have it do way too much. Thankfully, you've kinda sorta designed your code in such a way that it shouldn't be too hard to break it up into several directives. Basically, each of your handle functions should be its own directive.
So you'd have something like:
.directive('sidebarMenu', function(){
return {
template: 'path/to/sidebar/partial.html',
link: function(scope, elem, attrs){
// insert the code for your 'handleSidebarMenu()' function here
}
};
})
.directive('horizontalMenu', function(){
return {
template: 'path/to/horizontal/partial.html',
link: function(scope, elem, attrs){
// insert the code for your 'handleHorizontalMenu()' function here
}
};
})
and then your view would look something like:
<body ui-view="dashboard-shell" responsive-theme>
<div class="page-container">
<div class="page-sidebar nav-collapse collapse">
<horizontal-menu></horizontal-menu>
<sidebar-menu></sidebar-menu>
</div>
<div class="page-content" ui-view="dashboard">
</div>
</div>
</body>
And then you don't need a SidebarmenuController because your controller functions shouldn't be handling DOM elements like the sidebar. The controller should just handling the data that you're going to display in your view, and then the view (or .html file) will handle the displaying and manipulation of that data by its use of the directives you've written.
Does that make sense? Just try breaking that huge directive up into many smaller directives that handle specific elements or specific tasks in the DOM.

Wait until div is not visible to process next line

I need to write some code which is supposed to wait until a predefined div is no longer visible in order to process the next line. I plan on using jQuery( ":visible" ) for this, and was thinking I could have some type of while loop. Does anyone have a good suggestion on how to accomplish this task?
$( document ).ready(function() {
$(".scroller-right" ).mouseup(function( event ) {
alert('right');
pollVisibility();
});
});
function pollVisibility() {
if ($(".mstrWaitBox").attr("visibility")!== 'undefined') || $(".mstrWaitBox").attr("visibility") !== false) {
alert('inside else');
microstrategy.getViewerBone().commands.exec('refresh');
} else {
setTimeout(pollVisibility, 100);
}
}
$( document ).ready(function() {
$(".scroller-right" ).mouseup(function( event ) {
alert('right');
pollVisibility();
});
});
function pollVisibility() {
if (!$(".mstrWaitBox").is(":visible")) {
alert('inside if');
microstrategy.getViewerBone().commands.exec('refresh');
} else {
setTimeout(pollVisibility, 100);
}
}
div when not visible:
<div class=​"mstrWaitBox" id=​"divWaitBox" scriptclass=​"mstrDialogImpl" dg=​"1" ty=​"edt">​
</div>​
div when visible:
<div class=​"mstrWaitBox" id=​"divWaitBox" scriptclass=​"mstrDialogImpl" dg=​"1" ty=​"edt" visibility="visible">​
</div>​
You can use the setTimeout function to poll the display status of the div. This implementation checks to see if the div is invisible every 1/2 second, once the div is no longer visible, execute some code. In my example we show another div, but you could easily call a function or do whatever.
http://jsfiddle.net/vHmq6/1/
Script
$(function() {
setTimeout(function() {
$("#hideThis").hide();
}, 3000);
pollVisibility();
function pollVisibility() {
if (!$("#hideThis").is(":visible")) {
// call a function here, or do whatever now that the div is not visible
$("#thenShowThis").show();
} else {
setTimeout(pollVisibility, 500);
}
}
}
Html
<div id='hideThis' style="display:block">
The other thing happens when this is no longer visible in about 3s</div>
<div id='thenShowThis' style="display:none">Hi There</div>
If your code is running in a modern browser you could always use the MutationObserver object and fallback on polling with setInterval or setTimeout when it's not supported.
There seems to be a polyfill as well, however I have never tried it and it's the first time I have a look at the project.
FIDDLE
var div = document.getElementById('test'),
divDisplay = div.style.display,
observer = new MutationObserver(function () {
var currentDisplay = div.style.display;
if (divDisplay !== currentDisplay) {
console.log('new display is ' + (divDisplay = currentDisplay));
}
});
//observe changes
observer.observe(div, { attributes: true });
div.style.display = 'none';
setTimeout(function () {
div.style.display = 'block';
}, 500);
However an even better alternative in my opinion would be to add an interceptor to third-party function that's hiding the div, if possible.
E.g
var hideImportantElement = function () {
//hide logic
};
//intercept
hideImportantElement = (function (fn) {
return function () {
fn.apply(this, arguments);
console.log('element was hidden');
};
})(hideImportantElement);
I used this approach to wait for an element to disappear so I can execute the other functions after that.
Let's say doTheRestOfTheStuff(parameters) function should only be called after the element with ID the_Element_ID disappears, we can use,
var existCondition = setInterval(function() {
if ($('#the_Element_ID').length <= 0) {
console.log("Exists!");
clearInterval(existCondition);
doTheRestOfTheStuff(parameters);
}
}, 100); // check every 100ms

$(window).resize event not working 100% smoothly

I run the following code on a page (#home) that should smoothly inject a slider or if the page container is under 480px leave the page as is.
I cannot get the resize event to work 100% smoothly.
If I reduce the window the script (js.slide.js) wont get triggered but the content will be loaded in (slide.php). If I continue to reduce the window a little extra it will all work ok.
Could anyone please advise as to how I could get this working smoothly. The code is as follows
$(document).ready(function(){
if ($("#home").length > 0 ){
var homeSlideShow = {
$promoArea: $('#promo-area'),
$currentContent: $('#promo-area').contents(),
$pageContainer: $('.page'),
init: function(){
var hSS = homeSlideShow;
if (hSS.$pageContainer.width() > 480 ){
hSS.setTheSlideShow();
} else{
hSS.$promoArea.html(hSS.$currentContent);
}
},
setTheSlideShow: function(){
var hSS = homeSlideShow;
$.getScript(myscript_wp_vars.temp_dir + '/js/slide.js', function(){
hSS.$promoArea.load(myscript_wp_vars.temp_dir + '/libs/slide.php #c4u-slide',
function(){
var options = {
preloader: false,
nextButton: true,
prevButton: true,
animateStartingFrameIn: true,
transitionThreshold: 250
};
var sequence = $("#sequence").sequence(options).data("sequence"),
$slideShow = $("#c4u-slide");
});
});
}
};
//
// Check page size
//
if (homeSlideShow.$pageContainer.width() > 480 ){
homeSlideShow.setTheSlideShow();
}
//
// On window resize
//
$(window).resize(function() {
homeSlideShow.init();
});
}// END home.length
});//End $(document).ready(function()
Thanks in advance for any assistance or advice.
Cheers
Noel
window.resize event is triggered multiple times, depending of browser's behaviour. I'll suggest you to try this:
var timeoutResize;
$(window).resize(function(){
if(typeof timeoutResize != 'undefined') clearTimeout(timeoutResize);
timeoutResize = setTimeout(function(){homeSlideShow.init();},50);
});

Categories

Resources