I am attempting to build a small web application but I'm not clear how to communicate between different parts of the page. I will use my specific functionality to demonstrate my question.
All of my pages use a generic toolbar. Since this toolbar is used on multiple pages, it is in its own PHP file, which I include using
<?php include("../toolbar.php"); ?>
The toolbar includes a log in button. When clicked, this opens a modal login dialog (in this case the Facebook login dialog). When a user logs in, his or her name is displayed in the toolbar, and the log in button is replaced by a 'logout' button. Since this behavior is the same no matter which page the user is viewing, I created a toolbar.js file to display the log in modal and update the username/button appropriately.
However, on most pages, logging in or out from the toolbar needs to also update the contents of the main page. I can not guarantee that every page that includes toolbar.php will have to do anything when the log in status changes, but most will.
Similarly, the reverse is possible - a certain page might have a 'log in' button outside the toolbar. When this is used, the toolbar needs to update.
What is the best way to handle this?
Current implementation - this might be awful…
In toolbar.js I am basically calling a function, 'userSignedIn', whenever the user logs in (and an equivalent for log out). toolbar.js, implements this itself (it needs to update its button and user name label).
Then, if the main page (lets call it mainPage.php) needs to anything additional, I am re-using this same function to 'tack on' the additional actions. On load of that page, I do the following:
var originalUserSignedIn = userSignedIn;
userSignedIn = function() {
originalUserSignedIn();
customUserSignedIn();
}
customUserSignedIn is a function within mainPage.js, where I perform the additional actions. I have not yet implemented a solution for the opposite (sign in from mainPage.php needs to update toolbar.php).
I guess coming from an objective-C background, I am attempting something analogous to calling 'super' in a method implementation.
One way to do it is to initialize an empty array to hold callback functions and the sign function to call them, before any of the other javascript:
var userSignedInCallbacks = [];
var userSignedIn = function() {
for (var i = 0; i < userSignedInCallbacks.length; i++) {
userSignedInCallbacks[i]();
}
}
Then, toolbar and main page js will both just add their appropriate callbacks to the array:
userSignedInCallbacks.push(function() {
// ...
});
Finally, the login actions in either the toolbar or main page will both just call userSignedIn().
One way you could do this is by using the publisher-subscriber pattern for handling communications between different modules in your page so that every modules is only coupled on an event interface.
I created a very simple example here. It doesn't handle the logout process but you can still see how things could be structured.
HTML
<div id="toolbar">
<button class="login">Login</button>
</div>
<div id="another-section">
<button class="login">Login</button>
</div>
<div id="login-dialog">
<button class="login">Do login!</button>
</div>
JS
//in EventBus.js
var EventBus = {
subscribers: {},
publish: function (eventName) {
var args = Array.prototype.slice.call(arguments, 1),
subscribers = this.subscribers[eventName] || [],
i = 0,
len = subscribers.length,
s;
for (; i < len; i++) {
s = subscribers[i];
s.fn.apply(s.scope, args);
}
},
subscribe: function (eventName, fn, scope) {
var subs = this.subscribers[eventName] = this.subscribers[eventName] || [];
subs.push({ fn: fn, scope: scope });
}
};
//in toolbar.js
function Toolbar(el, eventBus) {
var $el = this.$el = $(el),
$loginButton = $('button.login', $el);
$loginButton.click(function () {
eventBus.publish('userWantsToLogin');
});
eventBus.subscribe('userLoggedIn', function () {
$loginButton.html('Logout');
//change button handlers to handle logout...
});
}
//in another-section.js
function AnotherSection(el, eventBus) {
var $el = this.$el = $(el),
$loginButton = $('button.login', $el);
$loginButton.click(function () {
eventBus.publish('userWantsToLogin');
});
eventBus.subscribe('userLoggedIn', function () {
$loginButton.html('Logout');
//change button handlers to handle logout...
});
}
//in main.js
$(function () {
var $loginDialog = $('#login-dialog');
$loginDialog.dialog({ autoOpen: false});
$('button.login', $loginDialog).click(function () {
EventBus.publish('userLoggedIn');
});
EventBus.subscribe('userWantsToLogin', function () {
$loginDialog.dialog('open');
});
new Toolbar('#toolbar', EventBus);
new AnotherSection('#another-section', EventBus);
});
Related
Here is the code I have currently,
<div class="panel">
<?php
if(isset($Uniid)) {
if (isset($from)) {
$url='Inevent.php';
include("display$category.php");
}
}
?>
</div>
<script>
document.addEventListener("DOMContentLoaded", function(event) {
var acc = document.getElementsByClassName("accordion");
var panel = document.getElementsByClassName('panel');
for (var i = 0; i < acc.length; i++) {
acc[i].onclick = function() {
var setClasses = !this.classList.contains('active');
setClass(acc, 'active', 'remove');
setClass(panel, 'show', 'remove');
if (setClasses) {
this.classList.toggle("active");
this.nextElementSibling.classList.toggle("show");
}
}
}
function setClass(els, className, fnName) {
for (var i = 0; i < els.length; i++) {
els[i].classList[fnName](className);
}
}
});
</script>
The main class which is the accordian is displayed currently, but when I click on the accordian, is when I want the panel to be executed, how do I go about doing it.
You can out your php code in a separate file and then call it with AJAX.
If you name your php file "loadPanel.php" then the AJAX request would look like this:
$.ajax({
url: "loadPanel.php"
}).done(function(response) {
$( '.panel' ).html( response );
});
Then add whatever php code you want inside you panel div to the loadPanel.php file.
The documentation for ajax is on the jQuery site here: http://api.jquery.com/jquery.ajax/.
There is a solution that isn't well known, but very powerful that consist in updating a part of the page with jQuery (works with the latest version of jQuery). So, to do that, you don't even have to create another page, so you just have to use the jQuery load() function.
This method is the simplest way to fetch data from the server. It is roughly equivalent to $.get(url, data, success) except that it is a method rather than global function and it has an implicit callback function. When a successful response is detected (i.e. when textStatus is "success" or "notmodified"), .load() sets the HTML contents of the matched element to the returned data. This means that you can use this method like this:
$( ".panel" ).load( window.location.href );
Im attempting to log the user activity in my database when they login and logout of the site. This works fine, but when they close the page via a tab or browser, there is no way to run the logout query, so according to the records the user never logs out of the site.
Method 1:
I have tried onbeforeunload, but this does not seem to trigger before the page closes.
Method 2:
I would try the method of using an ajax keepalive token sent to the php, but this would need to run every 1 min and could cause high traffic load.
Method 3:
I was hoping an alterantive would be to set the session.gc.maxlifetime to 1 min and add a call to the logout query via a destroy session callback.
Which would be the best method, or is there a better method of achieving this?
Is there a way to trigger a custom function(query) before php destroy session or php garbage collection has taken place?
UPDATE
I have taken the advise of everyone and decided to attempt 'method 1'. So far this is my attempt but its still not working perfectly:
var isClosePage = true;
//detect f5 and backspace page navigation
$(document).on('keypress', function(e)
{
if (e.keyCode == 116)
{
alert('f5');
isClosePage = false;
}
if (e.keyCode == 8)
{
alert('backspace');
isClosePage = false;
}
});
//detect back and forward buttons
$(window).bind('statechange',function()
{
alert('back');
isClosePage = false;
});
//detect page button press
$('html').on('mouseenter', function()
{
console.log('mouse has enetered!');
isClosePage = false;
});
//detect browser buttons press
$('html').on('mouseleave', function()
{
console.log('mouse has left!');
isClosePage = true;
});
//make ajax call (logout) to server if above events not triggered
$(window).on('beforeunload', function(e)
{
if(isClosePage)
{
$.ajax(
{
url:'php/function/active-user.php?logout=ajax',
dataType: 'jsonp',
crossDomain: true,
async: false
});
//return 'some default message';
}
else
{
isClosePage = true;
}
});
Can anyone suggest any improvments to how I can make this work well?
When you speak of Method 1, you're doing it client-side, right?
The interface is window.onbeforeunload, and this is a usage example:
window.onbeforeunload = function(e) {
// call server script to log the user activity to database ...
};
The event window.onbeforeunload IS triggered before page is unloaded (tab/window/browser closed).
I would like to extend MarcoS's answer a little bit. window.onbeforeunload should work but this isn't the preferred way to set a handler for events. Since this is a property other scripts attaching to this event will overwrite your listener and your function will never get called.
The preferred way of registering to this event is as follows:
window.addEventListener('beforeunload', function (evt) {
}, false);
You could also attach a event listener to unload
window.addEventListener('unload', function (evt) {
}, false);
Hope this helps
I am having the Div with scroll bar.When User clicks the scroll bar I need to call the function based on the scroll bar click.How can I make a call?
I am using Jquery in the client and using the PHP in the server side.
I know how to make ajax calls and etc.Only think is I need to make this call when scroll bar is clicked in that div.
Is it possible to make the ID for the scroll bar in that div.
You can bind to the scroll event, it's not fired when the user clicks the scroll-bar but it is fired when the scroll-bar moves for any reason:
//wait for document.ready to fire, meaning the DOM is ready to be manipulated
$(function () {
//bind an event handler to the `scroll` event for your div element
$('#my-scroll-div').on('scroll.test-scroll', function () {
//you can do your AJAX call in here, I would set a flag to only allow it to run once, because the `scroll` event fires A LOT when scrolling occurs
});
});
Note that .on() is new in jQuery 1.7 and in this case is the same as using .bind().
To set a flag like I suggest above:
$(function () {
var AJAX_flag = true;
$('#my-scroll-div').on('scroll.test-scroll', function () {
if (AJAX_flag === true) {
AJAX_flag = false;
$(this).off('scroll.test-scroll');
$.ajax({...});
}
});
});
Update
The best solution to adding event handlers to dynamically created elements is to bind to them before adding them to the DOM:
function scroll_func () {
var AJAX_flag = true;
$(this).on('scroll.test-scroll', function () {
if (AJAX_flag === true) {
AJAX_flag = false;
$(this).off('scroll.test-scroll');//unbind the event handler since we're done with it
$.ajax({...});
}
});
}
$.ajax({
...
success : function (data) {
//note that this selector will have to change to find the proper elements for you, if you are unsure how to select, start by seeing what data is by doing a `console.log(data);`
$(data).find('#my-scroll-div').on('scroll', scroll_func).appendTo('#container-element');
}
});
I have several divs that a user can Minimize or Expand using the jquery toggle mothod. However, when the page is refreshed the Divs go back to their default state. Is their a way to have browser remember the last state of the div?
For example, if I expand a div with an ID of "my_div", then click on something else on the page, then come back to the original page, I want "my_div" to remain expanded.
I was thinking it would be possible to use session variables for this, perhaps when the user clicks on the expand/minimize button a AJAX request can be sent and toggle a session variable...IDK..any ideas?
There's no need for an ajax request, just store the information in a cookie or in the localstorage.
Here's a library which should help you out: http://www.jstorage.info/
Some sample code (untested):
// stores the toggled position
$('#my_div').click(function() {
$('#my_div').toggle();
$.jStorage.set('my_div', $('#my_div:visible').length);
});
// on page load restores all elements to old position
$(function() {
var elems = $.jStorage.index();
for (var i = 0, l = elems.length; i < l; i++) {
$.jStorage.get(i) ? $('#' + i).show() : hide();
}
});
If you don't need to support old browsers, you can use html5 web storage.
You can do things like this (example taken from w3schools):
The following example counts the number of times a user has visited a
page, in the current session:
<script type="text/javascript">
if (sessionStorage.pagecount) {
sessionStorage.pagecount=Number(sessionStorage.pagecount) +1;
}
else {
sessionStorage.pagecount=1;
}
document.write("Visits "+sessionStorage.pagecount+" time(s) this session.");
</script>
Others have already given valid answers related to cookies and the local storage API, but based on your comment on the question, here's how you would attach a click event handler to a link:
$("#someLinkId").click(function() {
$.post("somewhere.php", function() {
//Done!
});
});
The event handler function will run whenever the element it is attached to is clicked. Inside the event handler, you can run whatever code you like. In this example, a POST request is fired to somewhere.php.
I had something like this and I used cookies based on which user logged in
if you want only the main div don't use the
$('#'+div_id).next().css('display','none');
use
$('#'+div_id).css('display','none');
*Here is the code *
//this is the div
<div id = "<?php echo $user; ?>1" onclick="setCookie(this.id)" ><div>My Content this will hide/show</div></div>
function setCookie(div_id)
{
var value = '';
var x = document.getElementById(div_id);
var x = $('#'+div_id).next().css('display');
if(x == 'none')
{
value = 'block';
}
else
{
value = 'none';
}
console.log(div_id+"="+value+"; expires=15/02/2012 00:00:00;path=/")
//alert(x);
document.cookie = div_id+"="+value+"; expires=15/02/2012 00:00:00;path=/";
}
function getCookie(div_id)
{
console.log( div_id );
var i,x,y,ARRcookies=document.cookie.split(";");
for (i=0;i<ARRcookies.length;i++)
{
x=ARRcookies[i].substr(0,ARRcookies[i].indexOf("="));
y=ARRcookies[i].substr(ARRcookies[i].indexOf("=")+1);
x=x.replace(/^\s+|\s+$/g,"");
if (x==div_id)
{
return unescape(y);
}
}
}
function set_status()
{
var div_id = '';
for(var i = 1; i <= 9 ; i++)
{
div_id = '<?php echo $user; ?>'+i;
if(getCookie(div_id) == 'none')
{
$('#'+div_id).next().css('display','none');
}
else if(getCookie(div_id) == 'block')
{
$('#'+div_id).next().slideDown();
}
}
}
$(document).ready(function(){
get_status();
});
Look about the JavaScript Cookie Method, you can save the current states of the divs, and restore it if the User comes back on the Site.
There is a nice jQuery Plugin for handling Cookies (http://plugins.jquery.com/project/Cookie)
Hope it helps
Ended up using this. Great Tutorial.
http://www.shopdev.co.uk/blog/cookies-with-jquery-designing-collapsible-layouts/
Using a hashchange event I'm detecting when a user clicks the back button in a browser and changing the URL accordingly. Is there a better way to do this for pagination? I'm currently changing the URL after a user clicks my pagination control like so:
$(".pager").click(function(){
var start = null;
if ($.browser.msie) {
start = $(this).attr('href').slice($(this).attr('href').indexOf('#')+1);
}
else {
start = $(this).attr('href').substr(1);
}
$('#start').val(start);
$.post("visits_results.php", $("#profile_form_id").serialize(),
function(data) {
$('#search_results').html(data);
location.href = "#visits=" + start;
});
return false;
});
My javascript to detect the back button looks like this:
function myHashChangeCallback(hash) {
if (hash == "") {
$("#loadImage").show();
var no_cache = new Date().getTime();
$('#main').load("home.php?cache=" + no_cache, function () {
$("#loadImage").hide();
});
return false;
}
else {
// adding code to parse the hash URL and see what page I'm on...is there a better way?;
}
}
function hashCheck() {
var hash = window.location.hash;
if (hash != _hash) {
_hash = hash;
myHashChangeCallback(hash);
}
}
I currently plan on checking each hashtag and the value to see what page I should load unless there is a better more efficient way.
Any thoughts or suggestions?
The jQuery Address plugin does this very well. Once setup it provides a series of logical navigation events which you can hook into. It also has very good support for history.pushState() which eliminates the need for hashtags in newer browsers and has equally good fallback support for those browsers that do not support pushState.
A simple implementation would look like this:
// Run some code on initial load
$.address.init(function(e) {
// Address and path details can be found in the event object
console.log(e);
});
// Handle hashtag/pushState change events
$.address.change(function(e) {
// Do more fancy stuff. Don't forget about the event object.
console.log(e);
});
// Setup jQuery address on some elements
$('a').address();
To enable pushState() support pass an argument to the script like so:
<script type="text/javascript" src="jquery.address-1.3.min.js?state=/absolute/path/to/your/application"></script>