I have two php pages. page.php and loader.php. Loader.php pulls data from mysql to fill a progress bar and page.php contains a function to refresh loader.php every second. It works but it's rather ugly because you can see the css animation resetting every second.
I would prefer having the loading bar html in page.php along with all the other html for my page but A) how do I get the vars from loader.php into page.php and B) how do I update the div (or any object) on page.php without refreshing the page?
I looked into AJAX which seems like it can do what I want but I'm new to AJAX and programming in general. I'm already proud it is (somewhat) working to begin with.
page.php
<script>
var myTimer = null;
//executes a script that fills mysql with data
function fileExec() {
$.ajax({
url:"fileexec.php",
type: "post"
});
startRefresh();
}
//Shows progress bar
function refreshData() {
$('#container').load('loader.php');
}
function stopRefresh() {
clearInterval(myTimer);
}
function startRefresh() {
myTimer = setInterval(refreshData, 1000);
}
</script>
loader.php
<?php
//Code not shown sets up connection to mysql to pull progress
$progress1 = $row['progress'];
$script1 = $row['script'];
//Stop refresh after shell script has finished
if ($script1 == "stop") {
echo "<script>";
echo "stopRefresh();";
echo "</script>";
}
?>
//Update progress bar with variable
<html>
<body>
<div class="progress-bar-wrapper">
<div class="progress-bar">
<div id="myBar" class="progress-bar-fill in-progress" style="height:10px;width:<?php echo $progress1; ?>%"></div>
</div>
</div>
</body>
</html>
Update it using js:
$.post("loader.php", {
function: "getProgress"
}, function(data) {
progress = JSON.parse(data);
//use progress to change your bar with js
});
And in the php:
switch ($_POST['function']) {
case 'getProgress':
getProgress();
break;
}
function getProgress() {
//get the progress state
echo json_encode($progress);
}
Related
I am new to this, so far i was just using html form, click the submit button, the page was refreshing and the data was sented to the server (mySQL). But i learned Ajax (AJAX is a developer's dream) they are saying cause you can:
Read data from a web server - after the page has loaded
Update a web page without reloading the page
Send data to a web server - in the background
So i did a simple example. Let's say that i have set the sqlConnection.php
let input = document.getElementById("inputField");
document.getElementById("submitBtn").addEventListener("click", function (){
if(input.value == ""){
alert("empty field");
}else {
$(document).ready(function (){
$.ajax({
url: 'insert.php',
method: 'POST',
dataType: 'text',
data: { comment: input.value },
success: function (){
input.value = "";
}
});
});
}
});
function selectQuestions(){
let data = "true";
$("#comments").load("select.php");
}
setInterval(selectQuestions, 3000);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Ajax text</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.0.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-wEmeIV1mKuiNpC+IOBjI7aAzPcEZeedi5yW5f2yOq55WWLwNGmvvx4Um1vskeMj0" crossorigin="anonymous">
</head>
<body>
<div class="container py-3">
<input id="inputField" type="text" class="form-control text-left" aria-label="">
<div class="py-2 my-1">
<button id="submitBtn" value="addNew" class="btn btn-success">Submit</button>
</div>
</div>
<div class="container" id="comments">
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="main.js"></script>
</body>
</html>
select.php has this:
$sql = "SELECT * FROM data";
$result = $conn->query($sql);
if (mysqli_num_rows($result) > 0) {
while ($row = mysqli_fetch_assoc($result)) {
echo "<p>{$row['question']}</p>";
}
} else {
echo "No comments";
}
So my Question: Is this correct in order to see new results that came from the server ? I call the setInterval method every 3 seconds as you can see. Is this bad for the server ? For example if i upload this project to a server and 10 users are using it.. am i exhausting the server - consuming more space ?
Assuming your code is in main.js, you don't need the $(document).ready(), since at the bottom of the <body>, your code will only run after everything is ready anyway. Not to mention it's in the wrong place.
At first I thought you were using .load() wrong, since its a shorthand for the event handler .on('load'). But it turns out in jquery 3.0 they've added an ajax method also called .load(). This is dumb and going to cause alot of confusion, but I digress, use $.get() or $.getJson to get things from a server.
Hitting the server with an interval isn't necessarily bad, it's called polling and it's how the internet did full duplex communication for many years. The problem comes when you poll too often or want to update too much data or have too many users all polling at once. How much is too much all depends on the machine your server is running on. Websockets is definitely a better option, but quite a bit more complex to configure.
I took the liberty to re-write some things, since if you're using jquery, might as well use jquery.
let $input = $('#inputField');
$('#submitBtn').on('click', function(e) {
e.preventDefault(); //assuming this is in a <form>, default action is to reload the page
if ($input.val()) {
$.post('insert.php', { comment: $input.val() })
.done(function() {
$input.val('');
})
.fail(function(error) {
//error handling code
});
} else {
alert("empty field"); //alerts are annoying and thread blocking,
} //maybe use a different method to communicate with the user.
});
setInterval(function() {
$.get('select.php')
.done(function(select) {
//whatever you want to do with the response from this
})
.fail(function(error) {
//error handeling code
});
}, 3000);
On the php side, maybe just clean things up a bit so that it's more readable. You can use the ->fetch_all(MYSQLI_ASSOC) method instead of a while loop to get an associated array for the result. Then just array_map() and implode() that to build you html response.
<?php
$conn = new mysqli("configs and what not");
$statement = $conn->query("SELECT * FROM data");
$result = $statement->fetch_all(MYSQLI_ASSOC);
if (count($result) > 0) {
echo implode("", array_map(fn($row) => "<p>{$row["question"]}</p>", $result));
} else {
echo "No comments";
}
$conn->close();
I'm building a photography portfolio. Some of my images have nudity, so I want to hide those by default until the user clicks a "Toggle Worksafe Mode" button.
I can do it with a standard form post (and sessions), but that causes "confirm form resubmission" errors when the user backs or reloads. I'm trying to figure out an AJAX post instead to avoid that.
UPDATE: This is the working code. Please note that this does NOT work with the "slim" jQuery distro; that's one of the main reasons I was having trouble.
Image Index Page:
<?php
session_start();
if (!isset($_SESSION['Worksafe_Mode'] {
$_SESSION['Worksafe_Mode'] = 1;
}
?>
<!-- other page content -->
<script src="scripts/jquery-3.2.1.min.js"></script>
<!-- other page content -->
<button type="button" id="Worksafe_Button" name="Worksafe_Button">
Toggle Worksafe Mode
</button>
<script>
$('#Worksafe_Button').click(function() {
$.post("worksafe_mode_toggle.php")
.done(function(data) {
window.location.href = window.location.href;
});
});
</script>
<!-- other page content -->
<?php
$Connection = Connect();
$query = mysqli_query($Connection, 'SELECT uri, name, nsfw FROM images ORDER BY uri');
while($row = mysqli_fetch_assoc($image)) {
if ($_SESSION['Worksafe_Mode'] == 1 && $row['nsfw'] == 1) {
echo 'If you are over 18, toggle Worksafe Mode to view this image';
}
else {
echo '<img alt="'.$row['title'].'" src="../'.$row['uri'].'/s.jpg" srcset="../'.$row['uri'].'/m.jpg 2x">';
}
}
?>
worksafe_mode_script:
session_start();
if (isset($_SESSION['Worksafe_Mode'])) {
if ($_SESSION['Worksafe_Mode'] == 1) {
$_SESSION['Worksafe_Mode'] = 0;
}
else {
$_SESSION['Worksafe_Mode'] = 1;
}
}
I think ajax is a good approach in your case.
I might do something like display a page of SFW images as the default, along with the toggle button.
When they click the button it triggers an ajax request to the back-end that sets/un-sets the session value in toggleWorksafe.php. Finally it triggers a page refresh.
During the page refresh the PHP code checks whether the session variable is set and shows either the filtered or unfiltered set of images, and changes the button's text to match.
To implement:
Include jQuery in the <head> section (jQuery simplifies the ajax call):
<!DOCTYPE html>
<html>
<head>
<script src="https://code.jquery.com/jquery-3.2.1.min.js" integrity="sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=" crossorigin="anonymous"></script>
</head>
<body>
<?php
session_start();
if (!isset($_SESSION['Worksafe_Mode'])) {
$_SESSION['Worksafe_Mode'] = 'yes';
}
?>
<button id="workSafe" type="button" name="Worksafe_Toggle_Button">
<?php
if ($_SESSION['Worksafe_Mode'] == 'no') {
echo 'Hide NSFW images';
}
else {
echo 'Include NSFW images';
}
?>
</button>
<!-- display safe images by default -->
<?php
if ($_SESSION['Worksafe_Mode'] == 'no') {
echo '<br/><br/>Showing NSFW images';
}
else {
echo '<br/><br/>Showing safe images only';
}
?>
<!-- any other page content here -->
<script>
$('#workSafe').click(function() {
// ajax request to page toggling session value
$.post("/toggleWorksafe.php")
.done(function(data) {
window.location.href = window.location.href; // trigger a page refresh
});
});
</script>
</body>
</html>
toggleWorksafe.php:
<?php
session_start();
if (isset($_SESSION['Worksafe_Mode'])) {
if ($_SESSION['Worksafe_Mode'] == 'yes') {
$_SESSION['Worksafe_Mode'] = 'no';
}
else {
$_SESSION['Worksafe_Mode'] = 'yes';
}
}
else {
$_SESSION['Worksafe_Mode'] = 'yes';
}
?>
there are a couple of ways to do this and it related to how you hide or load you images.
1. simple method
if you don't care about the user's age, and just need to toggle, then you can do it with just a js variable, a cookie, and two version of link. with this, you don't hide images, but loads them. the filtering is done in the server, where you can use database query or a simple folder separation. for example:
var nsfw = read_cookie('nsfw', false); // not an actual js function, search for how to read cookie in js --- read cookie value, default to false
function loadImage(nsfw){
if (nsfw){
$.get('nsfw-image-list-url', function(resp){
// the url should return a json with list of image urls
var list = resp; // jQuery automatically parse json with the right MIME
list.forEach(function(val){
// insert image to page
$('#container').append('<img src="' + val + '/>');
});
});
} else {
$.get('sfw-image-list-url', function(resp){
// the url should return a json with list of image urls
var list = resp; // jQuery automatically parse json with the right MIME
list.forEach(function(val){
// insert image to page
$('#container').append('<img src="' + val + '/>');
});
});
}
}
and in you button click event:
nsfw = !nsfw;
// clear the image first if needed
$('#container').empty();
loadImage(nsfw);
2. another simple method, but not as simple as the #1
you can also do it with only one link that returns a list of images with the type of it, such as nsfw or other things.
note: this method still uses cookie
for example the returned list is like this:
[
{"url": "some-image-1.jpg", "nsfw": "true"},
{"url": "some-image-2.jpg", "nsfw": "false"},
{"url": "some-image-3.jpg", "nsfw": "true"},
{"url": "some-image-4.jpg", "nsfw": "false"},
{"url": "some-image-5.jpg", "nsfw": "false"},
{"url": "some-image-6.jpg", "nsfw": "true"}
]
then you just render it when the conditions are met.
function renderImage(nsfw){
$.get('image-list-url', function(resp){
list.forEach(function(val, key){
if (nsfw || !val.nsfw){
$('#container').append('<img src="' + val.url + '/>');
}
});
});
}
and many other methods that are too long to explain, such as using Angular, React, or Vue
still uses cookie for between reloads or backs, and does not regard user's age.
as for the session based approach, you only need that if you need to verify your users age
that is if you have a membership functionality with DOB (date of birth) data in your site, if so, you can use #KScandrett 's answer
Confirm form resubmission happens because you do not perform a redirect after a successful form submission.
Take a look at this wiki page to see how to do it right. https://en.wikipedia.org/wiki/Post/Redirect/Get
I would like to recreate several like button that saves count in a file.txt but that doesn't work :/
<?php
function getClickCount()
{
return (int)file_get_contents("counter.txt");
}
function incrementClickCount()
{
$counter = getClickCount() + 1;
file_put_contents("counter.txt", $counter);
}
?>
<link href="https://fonts.googleapis.com/css?family=Roboto" rel="stylesheet">
<script type="text/javascript">
var clicks = 0;
function onClick() {
clicks = 1;
document.getElementById("clicks").innerHTML = clicks;
};
</script>
<button type="button" onClick="onClick()" title="Vous aimez la couverture?" class="btn"><img id="heart" src="https://trello-attachments.s3.amazonaws.com/568304b85fa72dcb958a1edf/584acfc48b82595af77f2030/6257bf1efec79d5baf22309f8f327ce5/favorite.png" /></button>
<p><a id="clicks"><?php echo getClickCount(); ?></a></p>
DEMO HERE
Thanks in advance for your help, I am looking since days on the web to find it but I don't...
Alexander
counter.php
<?php
function getClickCount() {
return (int)file_get_contents("counter.txt");
}
function incrementClickCount() {
$counter = getClickCount() + 1;
file_put_contents("counter.txt", $counter);
}
if(!empty($_POST)) {
if($_POST['click'] == 'true') {
incrementClickCount();
echo getClickCount();
} else {
echo getClickCount();
}
}
?>
counter.txt
0
index.php
<html>
<head>
<title>Click Counter</title>
</head>
<body>
<button type="button" onClick="onClick()" title="Vous aimez la couverture?" class="btn"><img id="heart" src="https://trello-attachments.s3.amazonaws.com/568304b85fa72dcb958a1edf/584acfc48b82595af77f2030/6257bf1efec79d5baf22309f8f327ce5/favorite.png" /></button>
<p><a id="clicks"></a></p>
<script>
function onClick() {
loadClicks(true);
}
function loadClicks(isClicked) {
var click = isClicked === true ? true : false;
$.ajax({
url: 'counter.php',
type: 'POST',
data: {
'click': click
},
success: function(response) {
$('#clicks').text(response);
}
});
}
loadClicks(false);
</script>
</body>
</html>
Code Explanation
Whenever you click the button, there is an ajax request sent asynchronously in the background to counter.php. This PHP file receives request and process accordingly.
Here in the code, we send a single data to the PHP file in the ajax POST request which is a boolean data that is set based on the condition like if the button is clicked.
In PHP file, you will check a condition if the request is happened by button click or else other. If it is by button, you will increment the click and send the click counter value in response else you will only send the value.
You will notice I've called loadClicks function with the parameter true in onClick function and false outside the function meaning that I first call the loadClicks(false) as soon as the script is started its execution to load only the clicks value and later when I click the button loadClicks(true) is invoked meaning increment and fetch the value.
You will understand the code when you go through them carefully.
At first glance, I see 3 problems with your script.
1) You are mixing JavaScript and PHP. JavaScript runs on browsers and PHP runs on servers. If you want to exchange data between those parts of your script you need to make a server call from the JS part to the server, e.g. by using AJAX. A simple HTML request in JavaScript to a PHP script will work too.
2) Also your <button> tag needs to be embedded in a <form> should point to a script to be executed (can be the same script).
3) You never seem to call incrementClickCount(), at least not in the part shown here.
Suggestions
The would code everything in PHP and then address the other two points. Or you need to implement some form of client / server communication.
I'm trying to make a page which will generate a result-set from a complex database query & php parsing... but that's mainly beside the point... The main point is that this takes a minute or two to complete, and I'm hoping to display a progress bar rather then a generic gif animation "loading..." picture.
A breakdown would be...
User opens Page A.
Page A requests data from Page B (Most likely AJAX).
Page B processes the 100000+ or so entries in the database and parses them.
Page A shows a progress bar which shows roughly how far through the process is
Page B returns the result set.
Page A displays the result set.
I know how to return data to the ajax query, but my issue is I don't know how to continuously return data to show the status of the process (Eg. % of rows scanned).
I've looked into EventSource / Server-Sent-Events, which shows promise, I'm just not too sure how to get it working properly, or if there is a better way to do it.
I've tried making a quick little mock-up page, using just EventSource works fine, but when I split it up into an eventSource call (page which monitors a session variable for change), and an ajax request (the actual data sending/return) it falls apart.
I'm probably missing something obvious, or doing something stupidly wrong, but this is most of what I have anyway... Any help, suggestions, tips, or even suggestions of completely other ways to do it would be awesome :)
User page:
<!DOCTYPE html>
<html>
<head>
<title>Dynamic Progress Bar Example</title>
<script src="script.js"></script>
</head>
<body>
<input type="button" value="Submit" onclick="connect()" />
<progress id='progressor' value="0" max='100' style=""></progress>
</body>
</html>
Javascript
var es;
function connect() {
startListener();
$.ajax({
url: "server.php",
success: function() {
alert("Success");
},
error: function() {
alert("Error");
}
});
}
function startListener() {
es = new EventSource('monitor.php');
//a message is received
es.addEventListener('message', function(e) {
var result = JSON.parse(e.data);
if (e.lastEventId == 'CLOSE') {
alert("Finished!");
es.close();
} else {
var pBar = document.getElementById('progressor');
pBar.value = result;
}
});
es.addEventListener('error', function(e) {
alert('Error occurred');
es.close();
});
}
function stopListener() {
es.close();
alert('Interrupted');
}
function addLog(message) {
var r = document.getElementById('results');
r.innerHTML += message + '<br>';
r.scrollTop = r.scrollHeight;
}
Monitor PHP
<?php
SESSION_START();
header('Content-Type: text/event-stream');
// recommended to prevent caching of event data.
header('Cache-Control: no-cache');
function send_message($id, $data) {
$d = $data;
if (!is_array($d)){
$d = array($d);
}
echo "id: $id" . PHP_EOL;
echo "data: " . json_encode($d) . PHP_EOL;
echo PHP_EOL;
ob_flush();
flush();
}
$run = true;
$time = time();
$last = -10;
while($run){
// Timeout kill checks
if (time()-$time > 360){
file_put_contents("test.txt", "DEBUG: Timeout Kill", FILE_APPEND);
$run = false;
}
// Only update if it's changed
if ($last != $_SESSION['progress']['percent']){
file_put_contents("test.txt", "DEBUG: Changed", FILE_APPEND);
$p = $_SESSION['progress']['percent'];
send_message(1, $p);
$last = $p;
}
sleep(2);
}
?>
EDIT:
I've tried a different approach, where:
Page A AJAX calls page B, which runs the request, and saves the progress to a SESSION variable
Page A AJAX calls page C every 2 seconds, which simply returns the value of the session variable. This loop is terminated when it reaches 100
However, this is not quite working either. It seems that the two AJAX requests, or the two scripts server-side are not running simultaneously.
Looking at debug output: Both AJAX calls are executed at about the same time, but then the page B script runs to completion by itself, and -then- the page C script runs. Is this some limitation of PHP I'm missing???
more code!
Server (Page B) PHP
<?PHP
SESSION_START();
file_put_contents("log.log", "Job Started\n", FILE_APPEND);
$job = isset($_POST['job']) ? $_POST['job'] : 'err_unknown';
$_SESSION['progress']['job'] = $job;
$_SESSION['progress']['percent'] = 0;
$max = 10;
for ($i=0; $i<=$max;$i++){
$_SESSION['progress']['percent'] = floor(($i/$max)*100);
file_put_contents("log.log", "Progress now at " . floor(($i/$max)*100) . "\n", FILE_APPEND);
sleep(2);
}
file_put_contents("log.log", "Job Finished", FILE_APPEND);
echo json_encode("Success. We are done.");
?>
Progress (Page C) PHP
<?php
SESSION_START();
file_put_contents("log.log", "PR: Request Made", FILE_APPEND);
if (isset($_SESSION['progress'])){
echo json_encode(array("job"=>$_SESSION['progress']['job'],"progress"=>$_SESSION['progress']['percent']));
} else {
echo json_encode(array("job"=>"","progress"=>"error"));
}
?>
Index (Page A) JS/HTML
<!DOCTYPE html>
<html>
<head>
<title>Progress Bar Test</title>
</head>
<body>
<input type="button" value="Start Process" onclick="start('test', 'pg');"/><br />
<progress id="pg" max="100" value="0"/>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script type="text/javascript">
var progress = 0;
var job = "";
function start(jobName, barName){
startProgress(jobName, barName);
getData(jobName);
}
function getData(jobName){
console.log("Process Started");
$.ajax({
url: "server.php",
data: {job: jobName},
method: "POST",
cache: false,
dataType: "JSON",
timeout: 300,
success: function(data){
console.log("SUCCESS: " + data)
alert(data);
},
error: function(xhr,status,err){
console.log("ERROR: " + err);
alert("ERROR");
}
});
}
function startProgress(jobName, barName){
console.log("PG Process Started");
progressLoop(jobName, barName);
}
function progressLoop(jobName, barName){
console.log("Progress Called");
$.ajax({
url: "progress.php",
cache: false,
dataType: "JSON",
success: function(data){
console.log("pSUCCESS: " . data);
document.getElementById(barName).value = data.progress;
if (data.progress < 100 && !isNaN(data.progress)){
setTimeout(progressLoop(jobName, barName), (1000*2));
}
},
error: function(xhr,status,err){
console.log("pERROR: " + err);
alert("PROGRESS ERROR");
}
});
}
</script>
</body>
</html>
Debug: log.log output
PR: Request Made
Job Started
Progress now at 0
Progress now at 10
Progress now at 20
Progress now at 30
Progress now at 40
Progress now at 50
Progress now at 60
Progress now at 70
Progress now at 80
Progress now at 90
Progress now at 100
Job Finished
PR: Request Made
In similar cases, I usually do it this way:
Client sends AJAX request to Page B.
Important: On success, client sends the same request again.
On the initial request, Page B says: OK, THERE ARE 54555 RECORDS.. I use this count to initiate the progress bar.
On each of next requests, Page B returns a chunk of data. Client counts the size of chunk and updates progress bar. Also it collects chunks in one list.
On last request, when all data is sent, Page B says: THAT'S ALL and client renders the data.
I think, you've gotten the idea.
NOTE: you can request all chunks in parallel, but it is a complex way. Server (Page B) should also return a fixed chunksize in the initial response, then client sends TOTAL_COUNT / CHUNK_SIZE requests concurrently and combines the responses till the last request is completed. So it is much faster. You can use https://github.com/caolan/async in this case to do the code much more readable.
I'm making a project with the slides of text.
I have in the page a main div that contain the text of each slide.
I saved the content in a database.
I want do this operation:
When I click the arrow for view the next slide, PHP catch from database the content and save it in a variable.
Then with JQuery, I replace the maindiv's content with variable.
It is possible? If not, How can I do it?
Use a ajax request to simulate a post request, and parse the information with php. You will end up with someting like this:
<?php
if( isset( $_POST["slide"] ) {
// Database stuff
// Echo back
echo "Information that you need to get in jQuery";
}
?>
<script>
$("button.next_slide").click(function(){
var ajax;
if (window.XMLHttpRequest) {
ajax = new XMLHttpRequest();
} else {
ajax = new ActiveXObject("Microsoft.XMLHTTP");
}
ajax.onreadystatechange = function() {
if (ajax.readyState==4 && ajax.status==200) {
document.getElementById("some_element").innerHTML = ajax.responseText;
}
}
ajax.open("POST","this_page.php?slide=1&foo=bar&bar=foo",true);
ajax.send();
});
</script>
<button class="next_slide">Next slide</button>
<div id="some_element"></div>
Note: This could be done without jQuery, or with jQuery's $.ajax method. Read more here: http://api.jquery.com/jquery.ajax/
In my understanding, you want to load some data when user click on a particular button and then load it into a div we can achieve it via jQuery ajax and PHP.
HTML
<div class="main_div">
<ul id="list"><!-- Our content will load here --></ul>
Next
</div>
jQuery
jQuery(document).ready(function($){
$('.call_ajax').on('click', function(e){
e.preventDefault();
$.post('ajax.php', { get_slide: true }, function(response){
// We are telling jQuery to parse response as json because in ajax.php we have used json_encode function.
var data = jQuery.parseJSON(response);
$("#list").append('<li>' + data['slide'] + '</li>');
});
})
})
PHP ajax.php
if(isset($_POST['get_slide'])){
//Get slide from db and output it as json, but currently for example purpose we assume that we have a array.
$data['slide'] = 'Your slide text';
echo json_encode($data); die(); // We have used die because we don't won't our script to run any more.
}
I've not tested it, but it will work.