issue with Javascript in Rails View - javascript

In my Rails View template, I'm using some jQuery for tabbed panels functionality:
<section>
... content ommitted
</section>
<script>
$(document).ready(function () {
$('.accordion-tabs-minimal').each(function(index) {
$(this).children('li').first().children('a').addClass('is-active').next().addClass('is-open').show();
});
$('.accordion-tabs-minimal').on('click', 'li > a', function(event) {
if (!$(this).hasClass('is-active')) {
event.preventDefault();
var accordionTabs = $(this).closest('.accordion-tabs-minimal')
accordionTabs.find('.is-open').removeClass('is-open').hide();
$(this).next().toggleClass('is-open').toggle();
accordionTabs.find('.is-active').removeClass('is-active');
$(this).addClass('is-active');
} else {
event.preventDefault();
}
});
});
</script>
Because I'm also using this script in other View templates, and I want to organize my Javascript a bit better, I created a Javascript file (tabbed_panels.js) under app/assets/javascripts and moved the above script to the tabbed_panels.js file.
However now the panels on my page have no content the first time the page is loaded. Only when the page is refreshed, the panels get loaded with content.
Does someone have an idea what's going on and how this can be solved, so my tabbed panels have content at the first page load?
thanks for your help,
Anthony

Turbolinks
The likely issue you have will be that you're trying to load the $(document).ready function with Turbolinks running.
This simply won't work (if you're using Turbolinks), as since Turbolinks refreshes only the <body> tag of your page, it will typically prevent your JS from binding to the various elements in the DOM, as the JS has not been reloaded
The way to fix this is to develop your JS around Turbolinks (using Turbolinks' event handlers):
#app/assets/javascripts/tabbed_panels.js
var new_items = function() {
$('.accordion-tabs-minimal').each(function(index) {
$(this).children('li').first().children('a').addClass('is-active').next().addClass('is-open').show();
});
$(document).on('click', '.accordion-tabs-minimal li > a', function(event) {
if (!$(this).hasClass('is-active')) {
event.preventDefault();
var accordionTabs = $(this).closest('.accordion-tabs-minimal')
accordionTabs.find('.is-open').removeClass('is-open').hide();
$(this).next().toggleClass('is-open').toggle();
accordionTabs.find('.is-active').removeClass('is-active');
$(this).addClass('is-active');
} else {
event.preventDefault();
}
});
});
$(document).on("page:load ready", new_items);
This should allow your tabs to be populated on page load, regardless of whether Turbolinks is operating or not.
--
Unobtrusive JS
Something else to consider (you've already done this), is that you really need to use unobtrusive javascript in your application.
Unobtrusive JS basically means that you're able to abstract your "bindings" from your page to your Javascript files in the asset pipeline. There are several important reasons for this:
Your JS can be loaded on any page you want (it's DRY)
Your JS will reside in the "backend" of your app (won't pollute views)
You'll be able to use the JS to populate the various elements / objects you want on screen
It's always recommended you put your JS into separate files - including in the views sets you up for a big mess down the line

Related

Show all hidden elements when a page loads with override?

I have a page that forces some JS to load on a page that I need to override. I can load a separate JS file to do this. I want to have the page do the .show for any of the .below-the-folds on the page. I guess the best way to say it is, I want all the "more" things on the page to be expanded when the page loads, rather than making a person click more to see what's below the fold on all these.
This is the JS I need to override, I can't change it since it's loaded by the app automatically. There can be more than one of the lists hidden, I'm not sure how much harder that makes things.
function MoreFacets($more_facets_div) {
this.$more_facets_div = $more_facets_div;
this.bind_events();
};
MoreFacets.prototype.bind_events = function() {
var self = this;
self.$more_facets_div.find('.more').on('click', function (e) {
$(this).siblings('.below-the-fold').show();
$(this).hide();
});
self.$more_facets_div.find('.less').on('click', function (e) {
$(this).parent().hide();
$(this).parent().parent().find('.more').show();
});
};
$(function() {
$('.more-facets').each(function() {
new MoreFacets($(this));
});
});
It's loaded on the page and the HTML looks like this:
<h3>Additional filters: </h3>
<dl id="facets">
<dt>Collecting Area</dt>
<dd> Here's Something in the list</dd>
<dd> Here's the last in the list</dd>
<div class="more-facets">
<span class="more btn">∨ more</span>
<div class="below-the-fold">
<dd>Something That's hidden is here</dd>
<dd>Something more in this hidden list</dd>
So when the ∨ more is clicked is when the others below-the-fold appear, and that's what I want to load when the page loads. There's usually a few different lists like this on the page.
So I'm thinking what I need to do is something like run the ('.below-the-fold').show() for all the lists when the page loads?
Update A note to clarify: when the page loads now they're all hidden. I'd like them to all show when the page is loaded so no one has to click anything to have everything showing.
Another note based on another question below... It's loaded in a separate file, and I can load my file before that one. I do know that I can override other JS on the page, so I assume I can override this as well.
Based on your last edit, it sounds like you're already onto the fastest solution to your problem.
Please note, this will only work if the script is not loaded asynchronously, but if you have control of the order the scripts are loaded in, you can insert your script between the problem script and jQuery.
Your script can be something as easy as redefining the function it's using to something like this:
MoreFacets.prototype.bind_events = function() {
var self = this;
//Autostart in our open state without completely disabling the functionality
self.$more_facets_div.find('.below-the-fold').show();
self.$more_facets_div.find('.more').hide();
self.$more_facets_div.find('.more').on('click', function (e) {
$(this).siblings('.below-the-fold').show();
$(this).hide();
});
self.$more_facets_div.find('.less').on('click', function (e) {
$(this).parent().hide();
$(this).parent().parent().find('.more').show();
});
};
Now, that won't work if you don't have control over the script loading, but you might have hope even in that case, because document ready functions in jQuery are invoked in the order they're registered, so if you can't really control where your script is you might play with an alternative
$(function() {
$('.more-facets').each(function() {
$(this).find('.below-the-fold').show();
$(this).find('.more').hide();
});
});
The first will be cleaner, but the second is a fallback for more restrictive situations, and both should achieve your desired effect without completely removing the functionality, just changing the default state on load.

Rerun Jquery function onclick

I have a simple jquery script that changes the url path of the images. The only problem is the doesn't apply after I click the load more button. So I'm trying to do a workaround where it calls the script again after clicking the button.
<script type='text/javascript'>
$(document).ready(function ReplaceImage() {
$(".galleryItem img").each(function() {
$(this).attr("src", function(a, b) {
return b.replace("s72-c", "s300")
})
})
});
</script>
HTML
Load More
While Keith's answer will get you what you are looking for, I really can't recommend that approach. You are much better off with something like this.
<script type="text/javascript">
$(function() {
var replaceImage = function() {
$('.galleryItem img').each(function() {
$(this).attr('src', function(index, value) {
return value.replace('s72-c', 's300');
});
});
};
replaceImage();
$('.js-replace-image').on('click', replaceImage);
});
</script>
Using this html
<button class="js-replace-image">Load More</button>
By taking this approach, you do not expose any global variables onto the window object, which can be a point of issue if you work with other libraries (or developers) that don't manage their globals well.
Also, by moving to a class name and binding an event handler to the DOM node via JavaScript, you future proof yourself much more. Also allows yourself to easily add this functionality to more buttons very easily but just adding a class to it.
I updated the anchor tag to a button because of the semantics of what you need to do - it doesn't link out anywhere, it's just dynamic functionality on the page. This is what buttons are best served for.
I'd also recommend putting this in the footer of your site, because then, depending on your situation, you will already have the images updated properly without having to click the button. The only need for the button would be if you are dynamically inserting more images on the page after load, or if this script was in the head of your document (meaning jQuery couldn't know about the images yet).
I hope this helps, reach out if you have questions.

jquery function include only once?

I'm building single page application with jquery. so assume I have a sidebar like this
dashboard.html
order.html
and each time i click on it I load the content via ajax. It work fine but I go back and forth btw pages I notice the script got loaded twice or more. How to solve this?
Put the scripts and HTML in separate files. Then keep track of whether you've already loaded a script, and don't load it again.
var dashboard_js_loaded = false;
$("#dashboard").click(function() {
$("#content").load("dashboard.html", function() {
if (!dashboard_js_loaded) {
$.getScript("dashboard.js", function() {
dashboard_js_loaded = true;
});
}
});
});
Maybe a library like require.js can be used to manage this more generally. Or you can just write a simple function that keeps track of which JS files have been loaded in an object.

Rails 4 - Javascript doesnt work unless its put within script tags on page

I have an app where Im using a lot of unobtrusive javascript to append rails partials to my page.
The trouble I have is this: javascript if placed in the assets directory wont work.
In Chrome dev tools I can see its loaded but, nothing happens. In my Coffeescript file I've used the DOM ready alternative for turbolinks found here. But this has no effect.
Whats strange is: If I place the script within script tags on the loaded partial then it works.
For example: this coffeescript file wont function if the partial containing the tabs is loaded after the javascript is loaded.
var ready;
ready = function() {
$('tab').click ->
$('.tab').removeClass("active");
$(this).addClass("active");
panel = $(this).attr("data-panel")
$('.panel').hide();
$('#' + panel).show();
};
$(document).ready(ready);
$(document).on('page:load', ready);
Whereas this script placed at the bottom of the page with the tabs does work.
<script>
$('.tab').click(function() {
$('.tab').removeClass("active");
$(this).addClass("active");
var panel = $(this).attr("data-panel")
$('.panel').hide();
$('#' + panel).show();
});
</script>
My question is this: First why is this happening? Second, is there a way to overcome this issue?
You are missing the . from $(.tab).click listener.

Script works only on homepage

I have problem with adding class after scroll and it's really strange to me and here is why:
I used this script on multiple projects and never had this problem before. When I scroll down on home page, script works perefectly, class "Fix" is added to class "navigacija" and the social icons, menu and languages are fixed at top of the page. But on other pages this is not the case. Class "Fix" isn't added to class "navigacija" after scrolling 145px down. And what's more interesting, I insert very large image on purpose at this page and until page loads that image, my script works (try to scroll down before image is loaded). When page is fully loaded, script doesn't work anymore. I'm working in Joomla, I made my own template, I didn't install any modules, components or plugins. There are only Joomla's standard js files and my scripts that I used before with this script without any problem.
Here is the website I'm working on: http://investfarm.moderanweb.rs/
and here is the script:
$(function() {
var navigacija = $(".navigacija");
$(window).scroll(function() {
var scroll = $(window).scrollTop();
if (scroll >= 145) {
navigacija.removeClass('navigacija').addClass("Fix");
} else {
navigacija.removeClass("Fix").addClass('navigacija');
}
});
});
Please help, thanks in advance.
Try to change $ to jQuery if you are using jQueryNoConflict, and why is it working on homepage, I guess because jQuery library is loaded twice, before and after mootools library, so try this instead, and you should do the same for ToolTip and other stuffs :
jQuery(function() {
var navigacija = $(".navigacija");
jQuery(window).scroll(function() {
var scroll = jQuery(window).scrollTop();
if (scroll >= 145) {
navigacija.removeClass('navigacija').addClass("Fix");
} else {
navigacija.removeClass("Fix").addClass('navigacija');
}
});
});
For starters, start cleaning up the errors that show in the console.
You have multiple script tags that points to an HTML page not to a script.
<script type="text/javascript" src="/templates/investfarmimpexmd/js/jquery-2.1.1.js"></script>
<script type="text/javascript" src="/templates/investfarmimpexmd/js/wow.min.js"></script>
<script src="/js/wow.min.js"></script>
I don't know what you expected those to be loading, but it is not loading a script and is causing errors.
Perhaps these be marked type="text/template" so the browser doesn't try to execute them and you can use them as templates?
And, you have an error on this line of inline Javascript that indicates that jQuery is not loaded properly so you will have to find out why that is:
jQuery(window).on('load', function() {
new JCaption('img.caption');
});
And, you are loading multiple different versions of jQuery in the same page, but not managing how those different versions are used. You can't just load a version of jQuery, issue a jQuery.noConflict() and then load another version of jQuery. The first will be doing nothing at that point so if you needed it for something, it will not be working.

Categories