What I am trying to do is to call a function every time a person scrolls that checks the current class of container and adds +1 to the current value of the current data attribute and then toggles the class relative to the data attribute it is currently changing the class on scroll but giving a "NaN. I am already running this function on click and it works fine.
here is a fiddle.
http://jsfiddle.net/kaL63/1/
This is my function on scroll
var timeout;
$(window).scroll(function() {
if(typeof timeout == "number") {
window.clearTimeout(timeout);
delete timeout;
}
timeout = window.setTimeout( check, 100);
});
My html Looks like this
<div class="container year-1987" data-year-index="1987">
Some Content
</div>
the function I am calling right now that I think should work..
function check(){
var
animationHolder = $('.container'),
currentClass = animationHolder.attr("class").match(/year[\w-]*\b/);
var goToYear = $('.container').data('year-index');
var goToYear2 = parseInt(goToYear,1000) + 1;
animationHolder.toggleClass(currentClass + ' year-' + goToYear2);
animationHolder.attr('data-year-index', goToYear2);
}
My working code on click
$("a").click(function (e) {
e.preventDefault();
var
animationHolder = $('.container'),
currentClass = animationHolder.attr("class").match(/year[\w-]*\b/);
var goToYear = $(this).data('year-index');
animationHolder.toggleClass(currentClass + ' year-' + goToYear);
animationHolder.attr('data-year-index', goToYear);
I rewrote your check method:
function check() {
var $container = $('.container'),
currentYear = $container.data('m-index'),
nextYear = 1 + currentYear;
$container.removeClass('m-' + currentYear).addClass('m-' + nextYear);
$container.data('m-index', nextYear);
}
I made the following changes:
There is no need for the regular expression since we can generate the class name ourselves.
I am not sure why you were originally using two separate data-attributes (m-index and year-index), but I switched them to both match. If you need both of them, some more logic is needed to use year-index after the initial call.
I am now updating m-index via .data() rather than setting a data attribute.
This method seemed to work fine for me.
Related
I have a list of news and I can modify the news. When I modify one she stay at her inital position (if she was at place 5 she stay here). But when I click "modify this new" a form pop to the bottom of the page and then when I submit the modifications I want to scroll to this modified new. To do that I would use something like
find position where label.text() == titleModified
then I could do
window.scrollTo(0,result of the line above);
For the moment I tried to do document.getElementById but it always bring me to the top of the page...
Thank you for helping me
PS: there is a link on Plunker to see the structure of news : https://plnkr.co/edit/mLCxPYaBR56KkEOLNF8F?p=preview
and this is my JS for the modification:
'submit .modifyArticle'(event) {
event.preventDefault();
const target = event.target;
const textModif = target.textModif.value;
const titreModif = target.titreModif.value;
const photoModif = target.photoModif.value;
const idModif = Session.get('idTemp');
//test if values from the from are not empty or whitespaced
if ((/\S/.test(textModif))||(/\S/.test(titreModif))) {
console.log("2ème étape: dans body.js -> submit .modifyArticle");
Meteor.call('articles.modify',idModif,textModif,titreModif,photoModif);
Session.set('wantModif',false);
//here my new is modified so I want to scroll to her
//var titreModified actually contains the title after the modification but only for the first new...
setTimeout(function(){
var titreModified = document.getElementById("titreArticle");
var position = titreModified.offsetTop;
console.log("Y: " + position);
console.log("var titreArticleModif: "+ titreModified.textContent);
}, 50)
[...]
EDIT (i'll put the solution here but the real hero is #alexr101): First I had to add a class to my label <label class="titreArticle">{{titre}}</label>
then this is the JS:
`setTimeout(function(){
$('.titreArticle').each(function(i, obj) {
if(obj.textContent.includes(titreModif)){
alert("le titre devrait être: " + obj.textContent);
var position = obj.offsetTop;
window.scrollTo(0,position);
return false;
}
});
}, 20)`
the timeout is here because it's not 100% real-time and I had to wait until the new title was put in the DOM.
Try getting the y position of the element like so:
//get modified element
var titleModified = document.getElementById("modifiedElement");
//get y position of element through offsetTop function
var yPosition = titleModified.offsetTop;
//Set scroll amount
window.scrollTo(0, yPosition );
Would that work?
I have been working on this question for several days, and have researched it on SO as well as the web at large and was unable to find material that helped me solve it.
I am trying to create a weather app that can toggle the weather units displayed between Fahrenheit and Celsius. I start by appending the weather in Fahrenheit, and then I have created an event handler that conditionally changes the inner content of the associated element based on whether that element is currently displaying "F" or "C".
As it is, my app successfully loads with the Fahrenheit temperature, and toggles to Celsius on click, but it will not toggle back to Fahrenheit. I assume there is some issue with how the events are registered, but for the life of me I cannot figure it out.
Here is my code:
var fahr = document.createElement("a");
fahr.attr = ("href", "#");
fahr.className = "tempUnit";
fahr.innerHTML = tempf + "°F" + "<br/>";
$("#currentWeather").append(fahr);
var cels = document.createElement("a");
cels.attr = ("href", "#");
cels.className = "tempUnit";
cels.innerHTML = tempc + "°C" + "<br/>";
var units = document.getElementsByClassName("tempUnit");
$(".tempUnit").click(function() {
if (units[0].innerHTML.indexOf("F") != -1) {
$(".tempUnit").replaceWith(cels);
} else {
$(".tempUnit").replaceWith(fahr);
}
})
Thank you so much in advance! Happy to provide additional information if necessary.
Currently what you are using is called a direct binding which will only attach to element that exist on the page at the time your code makes the event binding call.
As you using replaceWith(), existing element is replaced with new element and event handlers are not attached with them.
You need to use Event Delegation using .on() delegated-events approach.
General Syntax
$(parentStaticContainer).on('event','selector',callback_function)
Example, Also use this i.e. current element context and use setAttribute() to update href element
$("#currentWeather").on("click", ".tempUnit", function() {
if (this.innerHTML.indexOf("F") != -1) {
$(this).replaceWith(cels);
}
else {
$(this).replaceWith(fahr);
}
})
var tempf = 212;
var tempc = 100;
var fahr = document.createElement("a");
fahr.setAttribute("href", "#");
fahr.className = "tempUnit";
fahr.innerHTML = tempf + "°F" + "<br/>";
$("#currentWeather").append(fahr);
var cels = document.createElement("a");
cels.setAttribute("href", "#");
cels.className = "tempUnit";
cels.innerHTML = tempc + "°C" + "<br/>";
$("#currentWeather").on("click", ".tempUnit", function() {
if (this.innerHTML.indexOf("F") != -1) {
$(this).replaceWith(cels);
} else {
$(this).replaceWith(fahr);
}
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="currentWeather"></div>
I'm currently building a small Todo list application using vanilla Javascript but I'm having some issues creating a delete button that onClick removes it's parent element.
From what I have read, when an onClick is called in Javascript the this keyword can be used to refer to the element that called the function. With this in mind I have the following code:
window.onload = initialiseTodo;
function addRecord(){
var title = document.getElementById('issueTitle');
var issueContent = document.getElementById('issueContent');
var contentArea = document.getElementById('contentArea');
if(title.value.length > 0 && issueContent.value.length > 0){
var newItem = document.createElement('div');
newItem.id = 'task' + count++;
newItem.className = 'task';
newItem.innerHTML = '<div class="taskbody"><h1>' + title.value + '</h1>'+ issueContent.value + '</div><div class="deleteContainer">'
+ '<a class="delete">DELETE</a></div>';
contentArea.appendChild(newItem);
assignDeleteOnclick();
}
}
function deleteRecord(){
this.parentNode.parentNode.parentNode.parentNode.removeChild(this.parentNode.parentNode);
}
function assignDeleteOnclick(){
var deleteArray = document.getElementsByClassName('delete');
for(var i=0;i<deleteArray.length;i++){
deleteArray[i].onclick= deleteRecord();
}
}
function initialiseTodo(){
var btn_addRecord = document.getElementById('addRecord');
btn_addRecord.onclick = addRecord;
}
Basically I have a form that has two fields. When these fields are filled and the addRecord button is clicked a new div is added at the bottom of the page. This div contains a delete button. After the creation of this I assign an onclick event to the delete button which assigns the deleteRecord function when the delete button is clicked. My issue is with the deleteRecord function. I have used this to refer to the calling element (the delete button) and wish to remove the task div that is the outermost container however I current get a message that says: 'Cannot read property 'parentNode' of undefined ' which suggests to me the this keyword is not working correctly.
Any help would be greatly appreciated.
I've added the full code to a fiddle.
http://jsfiddle.net/jezzipin/Bd8AR/
J
You need to provide the element itself as a parameter. I did so by changing the html to include onclick="deleteRecord(this)" to make it a little easier to deal with. This means you can remove the assignDeleteOnclick() function
function deleteRecord(elem){
elem.parentNode.parentNode.remove();
}
Demo
You might style the .content to be hidden better if there are no elements to prevent that extra white space
Edit
Since you don't want an inline onclick, you can do it with js the same:
function deleteRecord(elem){
elem.parentNode.parentNode.remove();
}
function assignDeleteOnclick(){
var deleteArray = document.getElementsByClassName('delete');
for(var i=0;i<deleteArray.length;i++){
// Has to be enveloped in a function() { } or else context is lost
deleteArray[i].onclick=function() { deleteRecord(this); }
}
}
Demo
I have a table that is created in ASP.NET C# code behind. The table has several levels of groupings, and when I create the rows for the outer most grouping, I add an custom attribute as follows:
foreach (Table2Row row in Table2Data)
{
// skipping a bunch of irrelevent stuff
...
tr_group.Attributes.Add("RowsToToggle", String.Format(".InnerRowGroupId_{0}", row.GroupHeaderId));
...
}
The attribute is the CSS class name of the inner level rows that I would like to toggle. When the user clicks on the outer level row, I would like to call JQuery Toggle function for all inner level rows that match the custom attribute.
To achieve that effect, I have attached an onclick event to the header rows with the following script in the aspx file:
var tableId = '<%= Table2MainTable.ClientID %>';
$(document).ready(function () {
var table = document.getElementById(tableId);
var groupRows = table.getElementsByClassName("Table2GroupHeaderRow");
for (i = 0; i < groupRows.length; i++) {
table.groupRows[i].onclick = function () { ToggleOnRowClick(table.rows[i]); }
}
});
function ToggleOnRowClick(row) {
var r = $('#' + row.id);
var innerRows = r.attr('RowsToToggle');
$(innerRows ).toggle();
}
So, clicking anywhere on the header row should call the function ToggleOnRowClick, which should then toggle the set of rows below it via the custom attribute RowsToToggle.
When I set a (FireBug) break point in the ToggleOnRow function, the variable r appears to be pointing to the correct object. However, innerRows is not getting set but instead remains null. So am I setting the custom attribute incorrectly in ASP.NET or reading in incorrectly in JQuery?
You did not post the code to generate inner level rows, I am assuming you sat proper classes to them.
There are few issues with the jquery you posted. This line wouldn't work:
table.groupRows[i].onclick = function () { ToggleOnRowClick(table.rows[i]); }
You don't have any groupRows property defined for table object.
We don't care about table row anymore, we care about groupRows[i] and want to pass it to ToggleOnRowClick function.
This line in next function is also wrong:var r = $('#' + row.id);
Solution: Change your script to this:
var tableId = '<%= Table2MainTable.ClientID %>';
$(document).ready(function () {
var table = document.getElementById(tableId);
var groupRows = table.getElementsByClassName("Table2GroupHeaderRow");
for (i = 0; i < groupRows.length; i++) {
groupRows[i].onclick = function () { ToggleOnRowClick(this); }
}
});
function ToggleOnRowClick(row) {
//var r = $('#' + row.id);
var innerRows = $(row).attr('RowsToToggle');
$("." + innerRows).toggle();
}
I have tested the code with dummy data. So if you have any issue, PM me.
This line is your culprit:
table.groupRows[i].onclick = function () { ToggleOnRowClick(table.rows[i])
By the time the event handler runs, table.rows might still exist, but i will be set to groupRows.length+1, which is out of bounds for the array. The handler will get called with an argument of undefined.
Remember, Javascript is an interpreted language! The expression "table.rows[i]" will get interpeted when the handler runs. It will use the last value of i (which will still be set to the value that caused your for loop to end, groupRows.length+1).
Just use
table.groupRows[i].onclick = function () { ToggleOnRowClick(this) }
So, First you shouldn't use custom attributes... they are a sin!
Please use data attributes instead, so that is what I'm going to use in the code, should be an easy fix regardless.
If this doesn't work then I'd be very very interested in seeing a dumbed down HTML snippet of the actual output.
$(document).ready(function () {
$('#MYTABLE').on('click', '.Table2GroupHeader', function() {
var attr_if_you_insist_on_sinning = $(this).attr("RowsToToggle");
var data_if_you_like_not_sinning = $(this).data("RowsToToggle");
//if the row is like <tr data-RowsToToggle=".BLAH" or th etc
//asumming you set the attribute to .BLAH then:
var rows_to_toggle = $(data_if_you_like_not_sinning);
rows_to_toggle.toggle();
//assuming you set it to BLAH then:
var rows_to_toggle = $("."+ data_if_you_like_not_sinning);
rows_to_toggle.toggle();
});
});
$(document).ready(function () {
$('#<%= Table2MainTable.ClientID %> .Table2GroupHeader').each(function(){
$(this).click(function(){
$(this).toggle();
});
});
});
function init()
{
alert("init()");
/**
* Adds an event listener to onclick event on the start button.
*/
xbEvent.addEventListener(document.getElementById("viewInvitation"), "click", function()
{
new Ajax().sendRequest("31260xml/invitations.xml", null, new PageMaster());
xbEvent.addEventListener(document.getElementById("declinebutton"), "click", function ()
{
declineInvitation();
});
});
ok so what I have here is a event listerner function, the case is when viewInvitation is clicked , the program will fetch my xml file and run page master function where I created my decline button with id="declinebutton", however this does not work, the error message that i get is obj=null or the program could not find id = declinebutton, why is it so? I have created it when I called page master using dom. any help will be appreciated.
function PageMaster()
{
this.contentDiv = document.getElementById("content");
}
/**
* Builds the main part of the web page based on the given XML document object
*
* #param {Object} xmlDoc the given XML document object
*/
var subjectList;
var i;
PageMaster.prototype.doIt = function(xmlDoc)
{
alert("PageMaster()");
alert("Clear page...");
this.contentDiv.innerHTML = "";
if (null != xmlDoc)
{
alert("Build page...");
//create div Post
var divPost = document.createElement("div");
divPost.className = "post";
//create h1 element
var h1Element = document.createElement("h1");
var headingText = document.createTextNode("Invitations");
h1Element.appendChild(headingText);
//insert h1 element into div post
divPost.appendChild(h1Element);
subjectList = xmlDoc.getElementsByTagName("subject");
var groupList = xmlDoc.getElementsByTagName("group");
for (i = 0; i < subjectList.length; i++) //for each subject
{
var divEntry = document.createElement("div");
divEntry.className = "entry";
var subjectNum = subjectList[i].attributes[0].nodeValue;
var subjectName = subjectList[i].attributes[1].nodeValue;
var groupId = groupList[i].attributes[0].nodeValue;
var groupName = groupList[i].attributes[1].nodeValue;
var ownerId = groupList[i].attributes[2].nodeValue;
//set up the invitation table attributes
var table=document.createElement("table");
table.width = 411;
table.border = 3;
table.borderColor = "#990000"
var input=document.createElement("p");
var inputText=document.createTextNode("You are invited to join " + groupName + "(groupId : " + groupId +")");
input.className="style11";
var blank=document.createElement("nbps");
input.appendChild(inputText);
var acceptButton=document.createElement("input");
acceptButton.type="button";
acceptButton.id="acceptbutton";
acceptButton.value="accept";
var declineButton=document.createElement("input");
declineButton.type="button";
declineButton.id="declinebutton";
declineButton.value="decline";
table.appendChild(input);
table.appendChild(acceptButton);
table.appendChild(declineButton);
divEntry.appendChild(table);
var blankSpace = document.createElement("p");
divEntry.appendChild(blankSpace);
divPost.appendChild(divEntry);
}
//insert div post into div content
this.contentDiv.appendChild(divPost);
}
};
/**function getValueOf()
{
return i;
}**/
function declineInvitation()
{
alert("decline");
}
function acceptInvitation()
{
alert("hello");
/**var pos=getValueOf();
alert(subjectList[pos].attributes[0].nodeValue);**/
}
That's my page master function, and I definitely have created the button. but it does not work.
Try calling your function like this:
window.onload=init;
The javascript runs as the page loads. At that point, the element does not yet exist in the DOM tree. You'll need to delay the script until the page has loaded.
The example you gave doesn't create the "Decline" button, as your question suggests it should. If it should, you might want to look at that.
Of course, if the button already exists, please disregard this answer.
You have a listener inside a listener. Is that right?
What about this?:
function init(){
alert("init()");
/** * Adds an event listener to onclick event on the start button. */
xbEvent.addEventListener(document.getElementById("viewInvitation"), "click", function()
{
new Ajax().sendRequest("31260xml/invitations.xml", null, new PageMaster());
}
xbEvent.addEventListener(document.getElementById("declinebutton"), "click", function ()
{
declineInvitation();
});
As far as I understand, you create button with id="declinebutton" for each entry from xml, is that right?
If yes, I'd suggest you to generate different id's for each button (for example, append line index to 'declinebutton', so you have buttons 'declinebutton0', 'declinebutton1' an so on), and assign event listener to buttons separately in the loop.