How do I use javascript from content loaded with AJAX? - javascript

I have done a lot of researching in other questions and on Google, and so far nothing has worked. I have some JavaScript/jQuery experience and consider myself fluent in PHP.
I am developing an app for work that uses jQuery to load pages from the navigation menu. Here is the setup of that element so far:
<menu>
<button value="home">HOME</button>
<button value="process_sheets">PROCESS SHEETS</button>
<button value="admin">ADMINISTRATION</button>
<button value="faq">FAQ</button>
<div id="search-container"></div>
</menu>
<script>
$("menu button").each(function() {
$(this).on("click", function() {
$.get(null, {
page: $(this).val()
}, function(data) {
$("article#content").html($.parseHTML(data));
}, 'html');
});
});
</script>
The problem is that I have a simple accordion on one page that has code that isn't working when loaded through the menu.
Here is the accordion code:
$(function($) {
$('#accordion').find('.accordion-toggle').on('click', function(){
$(this).next().slideToggle('fast');
$(".accordion-content").not($(this).next()).slideUp('fast');
});
});
<h2>Process Sheets</h2>
<div id="process-sheets-accordion">
<h4 class="accordion-toggle">GE</h4>
<div class="accordion-content">
<?php include 'static_section_form.php'; ?>
</div>
<h4 class="accordion-toggle">5"</h4>
<div class="accordion-content">
<p>Vivamus facilisisnibh scelerisque laoreet.</p>
</div>
etc...
</div>

If the code in question is actually in the text returned by the ajax call, the problem is that that doesn't get executed when you load via get and then parse it and add it to the page.
jQuery has a function specifically designed for doing exactly that, though: load:
$("menu button").each(function() {
$(this).on("click", function() {
$("article#content").load(null, {page: $(this).val()});
});
});
load will retrieve the page using ajax, extract the scripts from it, put the content into the element, and then execute the scripts.
The above would use POST because we're passing an object as the second argument, but as you're using GET, we should just add page to the URL:
$("article#content").load(location.pathname + "?page=" + encodeURIComponent($(this).val()));
...or by passing it as a string:
$("article#content").load(location.pathname, "page=" + encodeURIComponent($(this).val()));
If you don't give a data object, load defaults to GET instead of POST.
Side note: I've never seen null as the URL for $.get before. I've copied it to the load call above, but I don't think null is valid (in either place), as there's nothing in the documentation saying it can be anything but a string. Use an actual URL, such as the location.pathname I've used in the GET example above.

Here is the working snippet of code which does exactly as I intended:
$("menu button").each(function() {
$(this).on("click", function() {
$("#content").load(window.location.pathname + "?page=" + encodeURIComponent($(this).val()));
});
});

Related

How can I use Ajax reload without breaking all jquery events?

I'm trying to reproduce the loading effect we can see on the google material.io website : when selecting a menu item, the content and url change, but the sidebar doesn"t move, as if only the content was changing..
can someone can explain me how to do this ? is it like the tehcnique described on css ticks tutorial "dynamic page replacing content" or something more like ajax ?
I have this code in html :
<div id="containerWrapper">
<div class="flex-row">
<div id="menu" class="flex-col-xs-12 flex-col-md-2 generator_menu">
<nav role="navigation" id="navigation" aria-label="Menu principal">
MY MENU HERE
</nav>
</div>
<div id="contentWrapper" class="flex-col-md-10">
<div id="guts">
<div class="flex-col-xs-12 generator_main">
MY CONTENT HERE
</div>
<div class="flex-col-xs-2">A sidebar dynamic TOC</div>
</div>
</div>
</div>
</div>
<footer></footer>
And the Ajax Code:
$("#menuList a").on('click',function(e){
e.preventDefault();
pageurl = $(this).prop('href');
$.ajax({url:pageurl,success: function(data){
$('#contentWrapper').html($(data).find('#guts'));
// Rerun Prism syntax highlighting on the current page
Prism.highlightAll();
return false;
}});
if(pageurl!=window.location){
window.history.pushState({path:pageurl},'',pageurl);
}
return false;
});
$(window).bind('popstate', function() {
$.ajax({url:location.pathname,success: function(data){
$('#guts').html(data);
}});
});
My problem:
-All my jquery events are in a dedicated file, wrapped in a jQuery(document).ready(function($) {}.
-If I put my ajax code like this, everything works except the ajax call..If I wrap my ajax code in a jQuery(document).ready(function($) {}., ajax works but all my other jquery events are broken when I click on any menu item and reload the page.
So is there a simple trick to make ajax works together with others jquery functions ?
Ok, finally I followed an answer given here..
In a js file, I wrapped my ajax call in a jQuery(document).ready(function(){}/
In a separate file, I wrapped all my jquery code in a function (scroll functions, clicks on menu function, filters, show/hide functions etc.):
function initialise(){}
Then in this same second file, at the end, I throw initialize function when document is ready, and when Ajax is complete :
$(document).ready(function(){
initialise();
});
$(document).ajaxComplete(function () {
initialise();
});
Now everything looks fine..
If there is a better way, I'll be glad to learn more on this..

Loading different pages while keeping the same header

I have a question, I'm working on my first portfolio with html, css and javascript.It's just a simple site with a header with the nav menu and the body with some of my info, I was wondering if there's a way besides iframe to load only the body everytime I click a link without affecting the header:
<header>
<nav>
Home, about me, etc
</nav>
</header>
<body>
this is my home page
</body>
To really get in to one page app development using a library like Angularjs really does the trick. If you just need something really simple you can use the jQuery load function. For instance:
<body>
<button id="home">Home</button>
<button id="about">About</button>
<button id="examples">Examples</button>
<div id="content">
this is my home page
</div>
</body>
<script language="Javascript">
$("#home").click(function() {
$( "#content" ).load( "home.html" ); //Load all retrieved content
});
$("#about").click(function() {
//Only load content from a specific node
$( "#content" ).load( "about.html #desc" );
});
$("#examples").click(function() {
//More specific loading of node
$( "#content" ).load( "examples.html #storeMain .container" );
});
</script>
This is the same question that started me off learning to program.
Things have gotten a lot better as far as loading dynamic content on the fly... but also - much more complicated as far as setup / build tools / JS frameworks etc.
People will say --- just use HTML or PHP / and that it doesn't matter if the whole page is reloaded and the header repaints... but those people aren't like you. What if you want to look at a picture of the band WHILE listening to a song (myspace)... - or you want your header to fade to a different background and do an animation...
Here is a PHP example that explains it all: https://css-tricks.com/dynamic-page-replacing-content
Here is a hacky JS way to do it / where all of the info is on one page... but is hidden and then shown with JS / but - the URL isn't going to change: https://codepen.io/sheriffderek/pen/zxmjgr
// build a reusable function that switches the "view"
var switchView = function(trigger) {
$('.view').addClass('hidden');
var currentSection;
currentSection = $(trigger).data('view');
$('.' + currentSection).removeClass('hidden');
console.log("you are currently on:" + currentSection);
};
// when you click the menu item... run that function to switch to the associated "view" based on it's data attribute
$('.view-controls a').on('click', function() {
switchView(this);
});
$('.header').on('click', function() {
$('.compact-menu').toggleClass('active');
});
$('.compact-menu a').on('click', function() {
$('.compact-menu').removeClass('active');
}); // I had to add this code just to post a codepen link : /
Example in action: http://bryanleebrowncomposer.com
Not ideal... but if you aren't going to have URL change anyway... this is actually better for SEO - and it's easy - and gets you the snappy style.
Here is a JavaScript framework way: https://www.codementor.io/sheriffderek/less-than-ambitious-websites-with-ember-js-5mtthoijp
I love Ember.js - but if you were going to try your hand at another framework - I'd take a look at this vue.js way: https://scotch.io/tutorials/how-to-build-a-simple-single-page-application-using-vue-2-part-1
All roads lead to pain and suffering - for the most part. Good luck! Post your outcome, will yah?
You could dynamically load your pages and inject it to your main page (single page).
As another option, which is partially not what you are looking for, you can load different pages via url and then dynamically rendering a header/footer onload (multiple page). But multiple pages will allow you to avoid having to manipulate URL states manually through something like domain.com?page=about and it's generally much more manageable in terms of regular website development.
If I make it single page, I load the body content via ajax. Like so
<div class="header">Lorem ipsum</div>
<div class="content-wrapper">
<!--Load content HTML here via ajax -->
</div>
<div class="footer">Lorem ipsum</div>
For multi-page setups, I do this
<div class="header-wrapper">
<!--Load header HTML here via ajax or render them via a javascript component -->
</div>
<div class="content-wrapper">Lorem ipsum</div>
<div class="footer-wrapper">
<!--Load footer HTML here via ajax or render them via a javascript component -->
</div>
The ajax part is as simple as this. (using jQuery ajax, axios, or http)
$.ajax({
type : 'GET',
url : 'foo.html',
dataType : 'html',
success : function(data){
wrapper.innerHTML = data;
},
Look up dynamically loading html via ajax so that you don't have to constantly repeat yourself implementing the same headers/footers across your pages. OR, like I said, you can also make/adopt a UI component to do it for you without ajax.

How to correctly append dynamic GetUIKit accordions?

I'm trying to dynamically append UIKit Accordions into a sortable list. The initial items (accordions) are working, but the dynamically appended is not working.
HTML
<div class="second-list" data-uk-observe>
<div class="uk-sortable uk-margin uk-accordion" data-uk-sortable="{group:'test'}" data-uk-accordion="{showfirst: false}">
<div class="uk-margin">
<div class="uk-panel uk-panel-box uk-accordion-title">Item 1
<button class="uk-button delete-btn">×</button>
</div>
<div class="uk-accordion-content">test1</div>
</div>
<div class="uk-margin">
<div class="uk-panel uk-panel-box uk-accordion-title">Item 2
<button class="uk-button delete-btn">×</button>
</div>
<div class="uk-accordion-content">test2</div>
</div>
</div>
</div>
JavaScript
// Remove item handler
$(".delete-btn").on("click", function () {
// 400 is default
$(this).closest(".uk-margin").fadeOut(400, function () {
$(this).remove();
});
return false;
});
function addItem () {
var $container = $(".second-list").find("[data-uk-sortable]");
$container.append(
`<div class="uk-margin">
<div class="uk-panel uk-panel-box uk-accordion-title">new item
<button class="uk-button delete-btn">×</button>
</div>
<div class="uk-accordion-content">description</div>
</div>`
);
}
addItem();
This is the minimal example I created to reproduce the problem. The sortable dynamic item is working fine (can be dragged), but the accordion doesn't. When clicking it, I'm getting:
Uncaught TypeError: Cannot read property 'data' of undefined
What I have tried:
Using data-uk-observe in the sortable element. I don't feel any difference using it.
Trying to initialize the accordion using the UIKit API:
UIkit.accordion($(".uk-margin"));
UIkit.accordion($(".uk-accordion-title"));
UIkit.accordion($(".uk-accordion-content"));
None of these doesn't fixe the problem.
So, how to correctly append dynamic GetUIKit accordions?
JSFIDDLE
It looks like what you want to do is:
Omit the data-uk-accordion attribute.
Save the object returned from calling UIkit.accordion(element, options).
After new accordion child elements have been added, call accordion.update(). (assuming you saved the returned object above in a variable called accordion)
For more information about how I arrived at that, check out the related issue on GitHub.
(also posted as an answer on UIKit accordion and ng-repeat doesn't work)
Try removing the accordion data from the element before you reinitialize it:
$(".uk-margin").removeData("accordion");
UIkit.accordion($(".uk-margin"));
That worked for me when I was writing an angular directive for this.
try update all items of accordion after add,
var component = UIkit.accordion($('.uk-accordion'));
component.update();
That worked for me.
I had the same issue but I'm using Meteor. This took me a good hour to get worked out, and I tried several different solutions, including using Template.onRender, Template.onCreated, but to no avail. They all seemed to attempt to initialize the UIKit accordion JS too soon.
I probably should've known better, but the right answer was to put #codermonkeyfuel's code before the end of my template helper, like so:
Template.appsList.helpers({
myApps: function() {
if (Meteor.user() ) {
console.log("we have a user: " + Meteor.user() );
var userId = Meteor.userId();
var user = Meteor.users.findOne(Meteor.userId());
console.log("userId: " + userId);
var apps = Meteor.users.findOne({_id: userId}, {
fields: {
myApps: 1
}
}).myApps;
// console.log(myApps);
return user && apps;
}
setTimeout(function() {
var accordion = UIkit.accordion($('.uk-accordion'), {collapse: false});
accordion.update();
}, 1000)
}
});
My head was thinking it was a timing issue with the rendering. In retrospect, the actual solution makes perfect sense. Hope it's useful for anyone using UIKit with MeteorJS.
meteor javascript uikit

How to make both href and jquery click event work

I have a reporting function answerCardInnerLinkReportingCall which gets invoked on click on <a> tag inside a specific div. I use event.preventDefault(); to override the default click behavior.
Currently I am redirecting the user to the target url in the reporting function after sending all the reporting parameters using window.open('http://stackoverflow.com/', '_blank'); method.
jQuery(document).on('click','#answerCard a', function(event) {
event.preventDefault();
answerCardInnerLinkReportingCall(this);
});
If I use onclick function in the tag I would have returned true and it would make href work without me redirecting the user manually but is it possible to do the same in click handler? I can't use onclick since I dont have control over the html data.
I wanted to check if there is a better way of implementing this?
Edit1: Adding sample HTML
<div class="answer" style="display: block;">
<div class="well">
<div id="answerCard" answercardid="check_phone_acard">
<h3 id="answerTitle">check your phone</h3>
<div><ol class="answerSteps"><li>Go to <a title="Link opens in a new window" href="https://test.com" target="_blank">Check phone</a>. If prompted, log in.</li></ol></div>
<label id="seeMoreAnswer">Displaying 1 of 1 steps. </label></div>
<!-- Utility Section -->
<div class="util">
<span class="pull-left"><a id="viewFull" href="/test.jsp?sid=52345">View full article ?</a></span>
<span class="pull-right">
</div>
</div>
</div>
</div>
I guess you dont need to use any 'event.preventDefault();' if you want to use links native functionality after the script executed.
try like this
jQuery(document).on('click','#answerCard a', function(event) {
//event.preventDefault();
alert('script running');
answerCardInnerLinkReportingCall(this);
});
also created JS Fiddle. check it out.
You can use javascript's:
window.location.href = 'http://url.here.com';
To tell the browser to navigate to a page. Hope it helps.
Other way can be of returning true or false from answerCardInnerLinkReportingCall and depending on that call or dont call event.PreventDefault();
Try something like this:
$('#answerCard a').click(function(event) {
var loc = $(this).attr('href');
event.preventDefault();
answerCardInnerLinkReportingCall(loc, this);
});
function answerCardInnerLinkReportingCall(loc, that){
// your code to do stuff here
window.open(loc, '_blank');
}
See this demo fiddle

JQuery maximum .load calls?

I am using the LiquidSlider framework and in each tab there is lots of HTML. So I decided to put the HTML into separate .html files to make the main page index.html cleaner.
Here is my HTML:
..
<head>
.. <-- Import jquery, slider files, etc -->
<!-- Import HTML from other files into divs -->
<script>
$(function(){
$("#about-content").load("about.html");
$("#other-content").load("other.html");
$("#help-content").load("help.html");
$("#contact-content").load("contact.html");
});
</script>
</head>
<body>
<section id="navigation">
..
</section>
<div class="liquid-slider" id="main-slider">
<!-- About -->
<div>
<h2 class="title">about</h2>
<div id="about-content"></div>
</div>
<!-- Other -->
<div>
<h2 class="title">other</h2>
<div id="other-content"></div>
</div>
<!-- Help -->
<div>
<h2 class="title">help</h2>
<div id="help-content"></div>
</div>
<!-- Contact -->
<div>
<h2 class="title">contact</h2>
<div id="contact-content"></div>
</div>
</div>
<section id="footer">
..
</section>
</body>
..
So when the document is loaded, theoretically the HTML would be loaded in via the .load calls right? It seems to work fine, until it gets to the very last tab (contact), where it just fails to load any content..
Odd right? I tried moving the divs around to see if it was a problem with my html files, but the last element always fails to load. Then I tried adding another tab, and the last two fail to load. This leads me to believe there is an upper-limit to the number of .load calls, capped at 3?
Anyone have any ideas or see any obvious problems? Or even suggest any better ways of achieving the same thing?
Thanks.
RTM, there's nothing there about a max number of calls, but there's a lot of information (and examples) of what kinds of callbacks you can use, which might just help you to diagnose the problem itself, for example:
$("#contact-content").load("contact.html", function( response, status, xhr )
{
if ( status == "error" )
{
var msg = "Sorry but there was an error: ";
console.log(xhr);//<-- check this
$( "#error" ).html( msg + xhr.status + " " + xhr.statusText );
}
});
As an alternative, just go for the old-school $.get call, since you don't seem to be passing any data to the server:
$.get( "contact.html", function( data )
{
$("#contact-content").html(data);
});
Another thing to consider might be: given that you're using liquidSlider, I take it not all of the content is visible from the off. Why not register a click handler, that .load's that content when the user actually clicks something? That does away with that series of load calls... Perhaps it's a concurrency issue of sorts. By that I mean: browsers restrict the number of concurrent AJAX requests that can be made.Perhaps you're running into that restriction, and have to wait for the requests to be completed? It's a long shot, but you never know... If you want to, check your browser here
But either way, using JS to fetch parts of the content dynamically is all well and good, but remember that I can switch off JS support in my browser. Or that, if your JS contains a syntax error, the script execution grinds to a halt, leaving me with a (half) empty page to gaze at.
Just using any server-side scripting language seems to me to be a better fit:
//index.php -- using PHP as an example
<div id="contact-content"><?php include 'contact.html'; ?></div>
After this gets processed by PHP, the response from the server will be a fully-fledged html page, that doesn't require any JS-on-the-fly loading. It'll almost certainly perform better, and still allows for cleaner html code on your server...
Server Side Includes would seem to me to be a better way of achieving the same thing. Use the right tool for the right job and all that.
<script>
var array = ['about', 'other', 'contact', 'help'];
for (i in array)
{
$('#'+array[i]).load(array[i]+'.html', function(){ });
}
</script>

Categories

Resources