jquery get href value and use it as a variable - javascript

So I have set up a scrolling code for my website.
<section class="fpage">
<a class="next" href="#view">test</a>
</section>
<section class="cpage">test</section>
In jquery I've got.
$(document).ready(function () {
$('.next').click(function (event) {
var cpage = $(this).find('a').attr('href');
var fpage = $(this).closest('section');
event.preventDefault();
fpage.addClass('anim').delay(500).queue(function(next){
$(this).removeClass().addClass('cpage');
next();
cpage.removeClass('.cpage').addClass('fpage');
});
});
});
I want the var:cpage to take the .next href value (in this case: #view) and use it as the name of it's variable. What have I dont wrong in this instance? And how can I turn the cpage var into #view?

.next is the a itself. you don't need to use .find
var cpage = $(this).attr('href');
OR
var cpage = this.href;
Edit : as cpage is an ID you need to do this to select the element
$(cpage).removeClass('.cpage').addClass('fpage');

As far as I understood, this should solve:
fpage.addClass('anim').delay(500).queue(function(next){
$(this).removeClass().addClass('cpage');
next();
$(cpage).removeClass('.cpage').addClass('fpage');
});

You have a few things out of place but all in all, no biggie.
First,
$('.next').click(function (event) {
var cpage = $(this).find('a').attr('href');
It can't call its self. .next is a class for the a tag, so how can a tag find inner elements with the a tag if it is the only a tag element? jQuery's .find works to find children and grandchildren and such of the element it's called upon. thus this line is just as easy written as:
var cpage = $(this).attr('href');
Second,
cpage.removeClass('.cpage').addClass('fpage');
cpage is only a string variable, not a jQuery object, thus it needs a slight change. Try:
$(cPage).removeClass
I don't know what version of jQuery you are using, but I gave your code a complete scrub down and edit and posted a jsFiddle using the latest jQuery's. Important to note, if you're using an older version, some things might be different, such as .on being .bind or .live in older versions.
jsFiddle
$(function() {
$(".next").on("click", function(e) {
e.preventDefault();
var cPage = $(this).attr("href"),
fPage = $(this).closest("section");
fPage.addClass("anim").delay(500).queue(function(n) {
$(this).removeClass().addClass('cpage');
n();
$(cPage).removeClass('.cpage').addClass('fpage');
});
});
})

Related

How would you make multiple buttons in html point to the same JS function

I want 'div#Welcome a' and 'div#Familiarize a' to point to this JS code:
e.preventDefault();
var itemClicked = $(this);
var id = itemClicked.attr("id");
id = id.replace(/\D/g,'');
slider.goToSlide(id);
return false;
right now i have this:
$('div#Welcome a').click(function(e){
and this:
$('div#Familiarize a').click(function(e){
for the click to go to the specified code. I was wondering if there was a way that anything that i wanted to be linked with this could could be without having to make a new function every time. Is there an ID for certain tags that i can put on?
Thank you in advance.
Make your code block into a named function and pass that to your event handlers:
function myEventHandler(e) {
e.preventDefault();
var itemClicked = $(this);
var id = itemClicked.attr("id");
id = id.replace(/\D/g,'');
slider.goToSlide(id);
return false;
}
$('div#Familiarize a').click(myEventHandler);
$('div#Welcome a').click(myEventHandler);
Or as #mark.hch and #Huangism have pointed out, you can also use comma-separated selectors:
$('div#Familiarize a, div#Welcome a').click(myEventHandler);
You can use multiple selectors together by separating them with comma.
$('div#Familiarize a, div#Welcome a').click(function(e) {
e.preventDefault();
var itemClicked = $(this);
var id = itemClicked.attr("id");
id = id.replace(/\D/g,'');
slider.goToSlide(id);
return false;
});
Although to simplify things, I suggest giving all the relevant DIVs a common class, and using that in the selector: $('div.tab a').click(...)
You could also give each div the same class and attach the handler to that.
<div class="thisclass">
$('.thisclass').click(function (e) {
}

Why would this JS fire on a form partial rendered on 1 page but not another?

I have a posts.js file that looks like this:
var ready;
ready = function() {
var toggleSidebar = $(".togglesidebar");
var primary = $("#primary");
var secondary = $("#secondary");
toggleSidebar.on("click", function(){
if(primary.hasClass("col-sm-9")){
primary.removeClass("col-sm-9");
primary.addClass("col-sm-12");
secondary.css('display', 'none');
}
else {
primary.removeClass("col-sm-12");
primary.addClass("col-sm-9");
secondary.css('display', 'inline-block');
}
});
};
var counter = function(event) {
var fieldValue = $(this).val();
var wc = fieldValue.trim().replace(regex, ' ').split(' ').length;
var regex = /\s+/gi;
var $wcField;
var maxWc;
if ($(this)[0] === $('#post_title')[0]) {
$wcField = $('#wordCountTitle');
maxWc = 7;
} else {
$wcField = $('#wordCountBody');
maxWc = 150;
}
$wcField.html(wc);
$wcField.toggleClass('over-limit', wc > maxWc);
};
$(document).ready(ready);
$(document).on('ready page:load', function () {
$('#post_title, #body-field').on('change keyup paste', counter);
});
In my application.html.erb page, I have this:
<div id="secondary" class="col-sm-3 sidebar" style="display: none;">
<aside>
<div class="about">
<h4>Submit Report</h4>
<%= render "posts/form" %>
</div>
</aside>
</div> <!-- /#secondary -->
And when I toggle this div, and the _form partial is displayed, the JS works fine in those fields.
But if I go to posts/new or /posts/:id/edit, it doesn't.
Even though if I check the source of that page, I see the post.js included there.
What could be causing this?
Edit 1
I am using Turbolinks, if that matters.
Edit 2
I tried this, per suggestions in the Answers:
var ready;
ready = function() {
// This is the Sidebar toggle functionality
var toggleSidebar = $(".togglesidebar");
var primary = $("#primary");
var secondary = $("#secondary");
$(document).on("click", ".togglesidebar", function(){
if(primary.hasClass("col-sm-9")){
primary.removeClass("col-sm-9");
primary.addClass("col-sm-12");
secondary.css('display', 'none');
}
else {
primary.removeClass("col-sm-12");
primary.addClass("col-sm-9");
secondary.css('display', 'inline-block');
}
});
};
But that hasn't worked.
The key issue I am having a problem with is the 'counters', i.e. #wordCountTitle and #wordCountBody don't work on my /posts/new even though they work on the posts/index when .toggleSidebar is activated.
Instead of binding directly to the element's onclick which needs careful handling of Turbolinks events, you can use an event handler on the document, try changing the direct event
toggleSidebar.on("click", function(){
to the delegated event
$(document).on("click", ".togglesidebar", function(){
When you modify the DOM dynamically (as when Turbolinks replaces it) if you use a direct event then you would need to re-assign it.
For a detailed explanation see http://api.jquery.com/on/#direct-and-delegated-events
The same that goes for the first function stands for the second. Also, with delegated events the "ready" check becomes unnecessary. With this in mind, your code would become:
$(document).on("click", ".togglesidebar", function(){
var primary = $("#primary");
var secondary = $("#secondary");
if(primary.hasClass("col-sm-9")){
primary.removeClass("col-sm-9");
primary.addClass("col-sm-12");
secondary.css('display', 'none');
}
else {
primary.removeClass("col-sm-12");
primary.addClass("col-sm-9");
secondary.css('display', 'inline-block');
}
});
$(document).on('change keyup paste', '#post_title, #body-field', function () {
var fieldValue = $(this).val();
var wc = fieldValue.trim().replace(regex, ' ').split(' ').length;
var regex = /\s+/gi;
var $wcField;
var maxWc;
if ($(this)[0] === $('#post_title')[0]) {
$wcField = $('#wordCountTitle');
maxWc = 7;
} else {
$wcField = $('#wordCountBody');
maxWc = 150;
}
$wcField.html(wc);
$wcField.toggleClass('over-limit', wc > maxWc);
});
I am using something like this on one of my projects, had the same problem hope it helps:
window.onLoad = function(callback) {
$(document).ready(callback);
$(document).on('page:load', callback);
};
and then wrap up my functions with onLoad, something like
onLoad(function() {
counter()
});
The onLoad function binds the ready event and the turbolink page:load event
when you do
$("selector").on("click", function(){});
you actually bind the selector to the click event.
White if you use
$(document).on("click", "selector", function(){});
You bind the click event to the document, which after the click checks if the clicked element was the selector you used. if yes, it executes the function. So you should use the second approach whenever binding events on dynamic elements.
I hope that answers the question of "why"
Long time I don't work with jQuery, but since I got here: If you have more than one element with the selector ".sidebar", I believe you'll need to use ".each" to bind the function to all elements that match that selector on the dom.
For the reference go here http://api.jquery.com/each/
Hope this helps, good luck.
I took a look on Turbolinks, and it does what I thought it did: It loads the HTML content of a link inside a container on the main page. Problem is, anything it loads is agnostic of whatever events and functions you have declared when the main page loaded, so indeed, after the first click, the selector on the HTML it has just loaded won't have the click event attributed to it (it was not on the DOM when you did the binding).
Possible solution: I did a little research on the .live() method, but is has been deprecated, so I recommend doing something like this:
$('body').on('click','a.saveButton', saveHandler)
Binding closer to the element you need will, it seems, will assure that whatever Turbolinks loads inside the body will get the bindings you have declared.
There is a more detailed answer here: Javascript event binding persistence
The documentation for the .live hook is here: http://api.jquery.com/live/#typefn
I used to have the same architecture on my web pages back in the day, and I did run on a problem similar to yours.
Hope it helps.
Should it work.. make sure:
Nothing id conflict.
Your html structure, maybe you forgot put value in your post.
Maybe wrong path posts.js, open with CTRL+U and then click post.js what is showing your code, if there so it's ok.
I try like this, it's ok(dummy):
$(document).on("click", ".togglesidebar", function(){
var primary = $("#primary");
var secondary = $("#secondary");
if(primary.hasClass("col-sm-9")){
primary.removeClass("col-sm-9");
primary.addClass("col-sm-12");
secondary.css('display', 'none');
}
else {
primary.removeClass("col-sm-12");
primary.addClass("col-sm-9");
secondary.css('display', 'inline-block');
}
});
$(document).on('change keyup paste', '#post_title, #body-field', function () {
var fieldValue = $(this).val();
var wc = fieldValue.trim().replace(regex, ' ').split(' ').length;
console.log(wc);
var regex = /\s+/gi;
var $wcField;
var maxWc;
if ($(this)[0] === $('#post_title')[0]) {
$wcField = $('#wordCountTitle');
maxWc = 7;
} else {
$wcField = $('#wordCountBody');
maxWc = 150;
}
$wcField.html(wc);
$wcField.toggleClass('over-limit', wc > maxWc);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="togglesidebar">Toogle</div>
<div id="wordCountTitle"></div>
<div id="wordCountBody"></div>
<div id="primary">
<div id="secondary" class="col-sm-3 sidebar" style="display: none;">
<aside>
<div class="about">
<h4>Submit Report</h4>
<input type="text" id="body-field"/>
</div>
</aside>
</div> <!-- /#secondary -->
</div>
Honestly with Js problems involving Turbolinks the best way to have it work efficiently is to install jquery rails gem, add // require jquery-Turbolinks and then remove //require turbolinks in your Js file
Make sure that the ids you are passing on the jQuery selectors are unique. If that page/partial is loaded without a postback, you should use
//document or selector that does not get removed
var primary = $(document).find('#primary');
var secondary = $(document).find('#secondary');
this, with
$(document).click('eventName', 'contextSelector', function)
should help resolve the issue.
If #post_title and #body-field are created dynamically you'll need to change:
$('#post_title, #body-field').on('change keyup paste', counter);
To this:
$(document).on('change keyup paste', '#post_title, #body-field', counter);
You'll need to delegate your events to elements that exist on page load (the document itself, in this case) when targeting elements that don't exist when the page is loaded (probably #post_title and #body-field).
Regarding $('#wordCountTitle') and $('#wordCountBody') on /posts/new, have you tried just typing in either of them at the console? It's possible that the view for /posts/new is different to your index and is missing those ids (or you made them classes or some other transposition happened).
I was having all kinds of issues with Turbolinks and jquery so a few suggestions on how I would go about trying to fix your problems:
Use gem 'turbolinks' to include it. Follow the instructions and do it the Ruby (gems) way rather than the PHP way.
Using Turbolinks means that$(document).ready doesn't fire when a new 'page' loads. The solution is to use: $(document).on('page:load', ready) along with $(document).ready(ready). The page:load event is the Turbolinks version of the ready event.
Others have suggested it but I've found it incredibly valuable in my rails app: Rather than binding events directly to selectors $("a").click(/* function */), binding to the document means that when Turbolinks loads a new page, the document binding survives the page load: $(document).on('click', 'a.particularAnchor', particularAnchorClicked)
With your specific code in mind, I would change this:
$('#post_title, #body-field').on('change keyup paste', counter);
To
$(document).on('change keyup paste', '#post_title, #body-field', counter);
It also seems to me that in your counter function you have mixed the following two lines up (regex needs to be defined first)
var wc = fieldValue.trim().replace(regex, ' ').split(' ').length;
var regex = /\s+/gi;

jQuery function works on click() not on ready()

I'm trying to change the 'href' attribute to match that of a sibling element.
$(function() {
$(".container").click(function() {
var href = $(this).find('.one').attr('href');w
$(this).find('.two').attr('href', href);
});
})
when the 'click();' event handler is used and I click on .container, the href of .two updates, however, when just using ready(); to execute it as soon as the page loads, it doesn't execute.
If you want to keep the context within .container element, just change click function to each, like this:
$(function() {
$(".container").each(function() {
var href = $(this).find('.one').attr('href');
$(this).find('.two').attr('href', href);
});
});
For more information about jQuery's each function, please refer here.
UPDATE
Also you can improve the performance a bit by caching the value of $(this), like this:
$(function() {
$(".container").each(function() {
var $this = $(this),
href = $this.find('.one').attr('href');
$this.find('.two').attr('href', href);
});
});
UPDATE 2
If you don't care about the context of .container, here is the code that will change href attributes of all those elements with class .two, which come after an element with class .one.
Here is the live example in JSFiddle.

adding specific class to element on click jquery

I'm having trouble with a simple nav bar that uses jQuery to add and remove a specific class when a certain page is active. I want a class to append to my aLink class depending on which ID is click. If I click on #aboutLink I want .linkActive to be added, but if I click on #sasLink I want .link2Active to be added. The tutorials I've looked at all have a single class being added, but since both my classes are different I need a specific one to be added depending on which ID is click.
HTML:
<div id="mainNav">
<ul id="nav">
<a id="mainLogo" href="/"><li></li></a>
<a id="aboutLink" class="aLink" href="/"><li></li></a>
<a id="sasLink" class="aLink" href="/savings-and-support"><li></li></a>
<a id="external" href="/"><li></li></a>
</ul>
</div><!--/#mainNav-->
I know my jQuery doesn't make sense, but it's all I could come up with. Logically I get it, but I'm lost on the syntax.
jQuery:
$(function () {
$(".aLink").click(function () {
if ($(this) == $("#aboutLink")
$(this).addClass('activeLink');
else $(this).addClass('active2Link');
});
});
Thanks for any input or direction.
var idToClass = {
'aboutLink' : 'linkActive',
'sasLink' : 'link2Active'
}
$('#nav a').click(function(){
e.preventDefault();
$(this).addClass(idToClass[this.id]);
});
JS Fiddle demo.
You could, instead, use toggleClass() to allow for those classes to be removed by a second click:
var idToClass = {
'aboutLink' : 'linkActive',
'sasLink' : 'link2Active'
}
$('#nav a').click(function(e){
e.preventDefault();
$(this).toggleClass(idToClass[this.id]);
});
JS Fiddle demo.
Edited in response to question, from the OP, in comments, below:
How would I remove the class so that both links don't appear to be active at the same time?
There's a few ways, but because you're adding different class-names to denote the 'active' state, they're a little inefficient. The first approach is to use a brute-force method, effectively looking for all a elements that have a class attribute and setting that attribute to the empty string, and then adding the linkActive/link2Active class-name to the clicked-on a element:
$('#nav a').click(function(e){
e.preventDefault();
var self = $(this);
self.closest('ul').find('a[class]').attr('class', '');
self.toggleClass(idToClass[this.id]);
});
JS Fiddle demo.
The alternative is to remove the specific classes from the elements who have their id listed in the idToClass object. This is, however, somewhat expensive in that it needs to iterate over the object, retrieving the id, finding the element with that id and then removing a class-name:
$('#nav a').click(function(e){
e.preventDefault();
for (var id in idToClass) {
if (idToClass.hasOwnProperty(id)){
$('#' + id).removeClass(idToClass[id]);
}
}
$(this).addClass(idToClass[this.id]);
});
JS Fiddle demo.
If, of course, you use a common class-name then it all becomes much easier:
$('#nav a').click(function (e) {
e.preventDefault();
var self = $(this);
self.closest('ul')
.find('.commonActiveClassName')
.removeClass('commonActiveClassName');
self.addClass('commonActiveClassName');
});
JS Fiddle demo.
References:
addClass().
closest().
event.preventDefault().
find().
removeClass().
toggleClass().
Since you already have ID tags to easily reference... I think you want something more like this?
$(function () {
$("#aboutLink").click(function () {
$(this).addClass('activeLink');
});
$("#sasLink").click(function () {
$(this).addClass('active2Link');
});
});
Try using this instead:
$(function () {
$(".aLink").click(function () {
var currentId = this.id;
if ( currentId == "aboutLink"){
$(this).addClass('activeLink');
else if( currentId == "sasLink") {
$(this).addClass('active2Link');
}
});
});

Is there an easier way to reference the source element for an event?

I'm new to the whole JavaScript and jQuery coding but I'm currently doing this is my HTML:
<a id="tog_table0"
href="javascript:toggle_table('#tog_table0', '#hideable_table0');">show</a>
And then I have some slightly ponderous code to tweak the element:
function toggle_table(button_id, table_id) {
// Find the elements we need
var table = $(table_id);
var button = $(button_id);
// Toggle the table
table.slideToggle("slow", function () {
if ($(this).is(":hidden"))
{
button.text("show");
} else {
button.text("hide");
}
});
}
I'm mainly wondering if there is a neater way to reference the source element rather than having to pass two IDs down to my function?
Use 'this' inside the event. Typically in jQuery this refers to the element that invoked the handler.
Also try and avoid inline script event handlers in tags. it is better to hook those events up in document ready.
NB The code below assumes the element invoking the handler (the link) is inside the table so it can traverse to it using closest. This may not be the case and you may need to use one of the other traversing options depending on your markup.
$(function(){
$('#tog_table0').click( toggle_table )
});
function toggle_table() {
//this refers to the element clicked
var $el = $(this);
// get the table - assuming the element is inside the table
var $table = $el.closest('table');
// Toggle the table
$table.slideToggle("slow", function () {
$el.is(":hidden") ? $el.text("show") : $el.text("hide");
}
}
You can do this:
show
and change your javascript to this:
$('a.tableHider').click(function() {
var table = $(this.name); // this refers to the link which was clicked
var button = $(this);
table.slideToggle("slow", function() {
if ($(this).is(':hidden')) { // this refers to the element being animated
button.html('show');
}
else {
button.html('hide');
}
});
return false;
});
edit: changed script to use the name attribute and added a return false to the click handler.
I'm sure this doesn't answer your question, but there's a nifty plugin for expanding table rows, might be useful to check it out:
http://www.jankoatwarpspeed.com/post/2009/07/20/Expand-table-rows-with-jQuery-jExpand-plugin.aspx

Categories

Resources