Can't select a child node within the Javascript code - javascript

I have this HTML piece of code
<div id="countdown">
<ul>
<li id="giorni"><span class="num red">66</span><span class="lab">Giorni</span></li>
<li id="ore"><span class="num red">23</span><span class="lab">Ore</span></li>
<li id="minuti"><span class="num red">44</span><span class="lab">Minuti</span></li>
<li id="secondi"><span class="num red">14</span><span class="lab">Secondi</span></li>
</ul>
</div>
I want to replace the content of each span that has a num class. In order to do that I have written:
var countdown = {
init: function( config ) {
this.dataX = Date.parse(new Date(2012, 5, 30));
this.adesso = new Date().getTime();
this.giorni = config.container.find('li#giorni');
console.log(this.giorni);
this.count();
},
... some other code ...
};
countdown.init({
container: $('div#countdown ul');
});
I really don't understand why if I try to log this.giorni I get [] in response, but if I write into the console the same code it returns the node I need.

$('#countdown span.num').each(function() {
$(this).empty().html(Math.random() * 1000);
});

Related

how to find descendent elements in javascript?

I have below mark-up . I am doing some DOM manipulation.I am trying to do using vanila js instead of jquery .
In jQuery i am writing like this
var $window = $(window),
$container = $('.rc105w1'),
$categories = $('.rc105w2'),
$counts = $('.rc105count'),
$productLists = $('.rc105w3'),
vdub = window.innerWidth,
resizeTimeout;
labelCategories();
function labelCategories() {
var row = 1,
prevoffset = 0,
guid = 0,
offset;
$categories.each(function() {
var $category = $(this),
$productList = $category.find($productLists),
$link = $category.find('> a');
console.log($productList.length,'product length')
console.log($link.length,'link length')
});
}
})
using this line $category.find($productLists) I am checking number of descendent or matching elements
I want to do same thing using vanilla js .I tried like this
function init(){
console.log('javascript code start')
var $window = window,
$container = document.querySelectorAll('.rc105w1'),
$categories = document.querySelectorAll('.rc105w2'),
$counts = document.querySelectorAll('.rc105count'),
$productLists = document.querySelectorAll('.rc105w3'),
vdub = window.innerWidth,
resizeTimeout;
labelCategories();
function labelCategories() {
var row = 1,
prevoffset = 0,
guid = 0,
offset;
$categories.forEach(function(el) {
var $category = el,
$productList = $category.querySelectorAll($productLists),
$link = $category.querySelectorAll('> a');
console.log($productList.length,'javasscript $productList length ')
console.log($link.length ,'javasscript $link length ')
});
}
}
function docReady(fn) {
// see if DOM is already available
if (document.readyState === "complete" || document.readyState === "interactive") {
// call on next available tick
setTimeout(fn, 1);
} else {
document.addEventListener("DOMContentLoaded", fn);
}
}
docReady(init)
I am not able to find matching element or descendent elements any way to find using javascript ?
<section class="rc105 rc105v0">
<div class="cwidth">
<div class="rc105w1">
<div class="rc105w2 rc105w2-bttn">
<a href="/cloud/migrate-applications-to--cloud/">
<span> Applications</span>
</a>
</div>
<div class="rc105w2 rc105w2-bttn">
<a href="/cloud/migrate-custom-applications-to-cloud/">
<span>Custom Applications</span>
</a>
</div>
</div>
<div class="rc105w1 rw-scrim-neutral-40bg">
<div class="rc105w2 rc105w2-drawer rc105w2-6up rc105w2-12set rc105w2-tallbttn">
<a href="#developer-services">
<span>Developer Services</span>
</a>
<div class="rc105w3">
<h3>Developer Services</h3>
<p>Build, deploy, and manage modern cloud applications using developer friendly tools and services.</p>
<div class="rc105w4">
<div class="rc105w5 rc105w5-half">
<h4>Build and run</h4>
<ul class="rc105linklist rc105w5-2col">
<li>API Gateway</li>
<li>API Management</li>
</ul>
</div>
<div class="rc105w5 rc105w5-quarter">
<h4>Low code</h4>
<ul class="rc105linklist">
<li>Application Express (APEX)</li>
<li>Visual Builder Studio</li>
</ul>
</div>
<div class="rc105w5 rc105w5-quarter">
<h4>Extend</h4>
<ul class="rc105linklist">
<li>Application Integration</li>
<li>Content Management</li>
</ul>
</div>
</div>
</div>
</div>
<div class="rc105w2 rc105w2-drawer rc105w2-6up rc105w2-12set rc105w2-tallbttn">
<a href="#containers-and-functions">
<span>Containers and Functions</span>
</a>
<div class="rc105w3">
<h3>Containers and Functions</h3>
<p>Deploy microservices applications on high-performance, managed open source Docker, Kubernetes, and Fn Functions services.</p>
<div class="rc105w4">
<div class="rc105w5">
<ul class="rc105linklist">
<li>Functions</li>
<li>Kubernetes Engine</li>
<li>Registry</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
querySelectorAll() expects a selector not a (static) Nodelist.
To make your code work you can just pass the selector like you do with $link.
$productList = $category.querySelectorAll('.rc105w3')
Maybe the problem occurs when you try to use querySelectorAll, because it will provide you an array, so try using querySelector instead of querySelectorAll and also querySelectorAll expects a selector not a Nodelist

Why document.querySelectorAll() returns null elements except the first one?

I need to select two elements (two tabs of five within a web) for click over them and scrap the tables returned.
Each tab is a 'li' and its child is a 'span' that contains the onclick event. I always get the list of tabs with the correct number of them but only the first element is not null, and even in the first element I can't call the onclick event to show me its tables. Also the 'id' that contains the onclick event is random.
I using phantomjs 2.1.1 and casperjs 1.1.4
JS code:
//Wait to be redirected to the Home page, and then make a screenshot
casper.then(function(){
casper.wait(5000, function(){
this.capture('home.png');
var tabs = casper.evaluate(function() {
//return document.querySelectorAll('[id^="dbTabLabel_"]'); //Another selector option
return document.querySelectorAll('span.dashTitle');
}
console.log('Num Tabs: ' + tabs.length););
for(i = 0; i < tabs.length; i++) {
if(tabs[i]) {
console.log('Form exists');
console.log('form id: ' + tabs[i].id);
// create a mouse click event
var event = document.createEvent( 'MouseEvents' );
event.initMouseEvent( 'click', true, true, window, 1, 0, 0 );
// send click to element
tabs[i].dispatchEvent( event );
var name = tabs[i].innerText + '.png'
casper.wait(2000, function(){ // Wait to load completely
this.capture(name); //Make a screenshot for each tab
});
} else {
console.log("Null Tab");
}
}
})
});
The output:
Num Tabs: 5
Form exists
form id: dbTabLabel_820718256523832
Null Tab
Null Tab
Null Tab
Null Tab
I want scrap this web (html code when I'm login in and save the web with getHTML casper function). The web screenshot here. And this is the fragment corresponding to the tabs:
<!--TEMPLATES-->
<ul id="tabul">
<li id="litab" class="ntabs add">+</li>
<li id="litab" class="add rightAlign setting-item">
<img src="/Content/images/icons/expand-24x24.png" class="out-triggerer gray" onclick="fullScreen()">
</li>
<li id="default-report-export" class="rightAlign">
<a href="/report/defaultExport" download="">
<input type="image" src="/Content/images/icons/excel.gif" value="Excel" title="Export default report">
</a>
</li>
<li id="default-report-export" class="rightAlign">
<a href="/report/defaultExport?isPdf=true" download="">
<input type="image" src="/Content/images/export-pdf-24x24.png" value="Excel" title="Export default report">
</a>
</li>
<li id="dbTab_889113733777776" class="ntabs addedTab activeTab">
<span id="dbTabLabel_889113733777776" class="dashTitle" onclick="clickDashboard('889113733777776')">Dashboard EUR</span>
<span id="dbTabSettings_889113733777776" class="settingsContainer dashSettings" style="">
<div id="topnav" class="topnav">
<a href="javascript:void(0)" class="signin" onclick="toggleTabSettingsMenu('889113733777776',true);">
<span><img src="/Content/Images/icon_gear.png" alt="Edit"></span>
</a>
</div>
<fieldset id="dbTabSettingsMenu_889113733777776" class="dashSettings-menu">
<ul class="dashboardEditMenu">
<img src="/Content/images/close.png" onclick="toggleTabSettingsMenu('889113733777776',false);" alt="tooltip" style="position:absolute;right:2px;top:2px;border:0;">
<li class="dashboardEditMenuList">
Añadir widgets
</li>
<li class="dashboardEditMenuList">
Borrar este dashboard
</li>
</ul>
</fieldset>
</span>
</li>
<li id="dbTab_894967889413237" class="ntabs addedTab">
<span id="dbTabLabel_894967889413237" class="dashTitle" onclick="clickDashboard('894967889413237')">Dashboard USD</span>
<span id="dbTabSettings_894967889413237" class="settingsContainer dashSettings" style="display:none;">
<div id="topnav" class="topnav">
<a href="javascript:void(0)" class="signin" onclick="toggleTabSettingsMenu('894967889413237',true);">
<span><img src="/Content/Images/icon_gear.png" alt="Edit"></span>
</a>
</div>
<fieldset id="dbTabSettingsMenu_894967889413237" class="dashSettings-menu">
<ul class="dashboardEditMenu">
<img src="/Content/images/close.png" onclick="toggleTabSettingsMenu('894967889413237',false);" alt="tooltip" style="position:absolute;right:2px;top:2px;border:0;">
...
</ul>
</fieldset>
</span>
</li>
</ul>
I don't know if my problem is if my problem is related to this post I have read. But the proposed solution I can't do it because my 'ids' are random and I can't make "static" selectors for get them.
it's not an issue with document.querySelectorAll() because you have the right number of tabs in console.log('Num Tabs: ' + tabs.length);
it's because of the asynchronous casper.wait() here's an answer about asynchronous process in a loop
The for loop runs immediately to completion while all your
asynchronous operations are started. When they complete some time in
the future and call their callbacks, the value of your loop index
variable i will be at its last value for all the callbacks.
one way to work this out is to use use es6's let instead of var like
for(let i = 0; i < tabs.length; i++) { ... ES6 var vs let
or use .forEach since it creates its own function closure
for vs .forEach()
replace your for(i = 0; i < tabs.length; i++) { with tabs.forEach(function(tab)) { ..
and access the tab with tab instead of tabs[i]
here's a snippet to demonstrate it :
var tabs = ['tab1', 'tab2', 'tab3', 'tab4']
for(var i = 0; i < tabs.length; i++){
if(tabs[i]){
setTimeout(function(){ // simulating casper.wait
console.log('in the for loop with var : ' , tabs[i]);
}, 1000);
}
else{
console.log('none');
}
}
for(let i = 0; i < tabs.length; i++){
if(tabs[i]){
setTimeout(function(){ // simulating casper.wait
console.log('in the for loop with let : ' , tabs[i]);
}, 1000);
}
else{
console.log('none');
}
}
tabs.forEach(function(e){
if(e){
setTimeout(function(){ // simulating casper.wait
console.log('in the forEach loop : ' , e);
}, 1000);
}
else{
console.log('none');
}
})

Losing context in callback when invoked the second time backbone

The following code works, but I think there's room for improvement. The index check is there because after the first element is removed the next element looks like it has an index of -1, but is actually the previously removed element. Then it iterates again and finds the clicked element and removes it. BUT since the index is -1 on the first go around the wrong group gets deleted.
How do I keep the zombie elements from being iterated on more efficiently? This is in a backbone view with an in page confirmation.Thanks.
EDIT: To add HTML
Group section always has a default group that shouldn't be deleted.
<div class="section-border-top--grey js-favorite-group">
<h4 class="expandable__cta cta--std-teal js-expand-trigger"><span class="icon icon-plus--teal expandable__cta-icon"></span>All Doctors</h4>
<div class="expandable__content js-favorite-doctor-row-container" aria-expanded="true">
<div class="location-section dr-profile">
<div class="section__content js-doctor-row">
<div class="favorite-doctor-manage__row">
DR info
</div>
</div><!--/section__content-->
</div><!--/location-section-->
</div><!--/expandable__content-->
Tag section to remove groups
<div class="js-favorite-doctor-manage-add-remove">
<div class="grid-construct">
<div class="expandable" data-controller="expandable">
<ul class="tag-list js-group-list" tabindex="-1">
<li class="tag tag--teal" >
Lauren's Doctors
<ul class="tag-sub">
<li><button class="tag-icon tag-icon--close-white js-group-remove">Remove group: Lauren's Doctors</button></li>
</ul>
</li>
<li class="tag tag--teal" >
Timmy's Doctors
<ul class="tag-sub">
<li><button class="tag-icon tag-icon--close-white js-group-remove">Remove group: Timmy's Doctors</button></li>
</ul>
</li>
</ul>
</div>
removeGroup: function( evt ) {
var deleteGroup = function() {
if ( $(evt.currentTarget).closest('.tag').hasClass('is-active')){
var clickedTag = $(evt.currentTarget).closest('.tag');
var groupList = this.$el.find('.js-group-list');
var groupTags = groupList.find('.tag');
var index = groupTags.index(clickedTag);
var groupSections = $('.js-favorite-group');
// add one to account for "All" section which is never removed
var groupToRemove = groupSections.eq(index + 1);
console.log(groupToRemove);
var removedGroupName = this.getGroupNameForSection(groupToRemove);
var allDoctors = groupSections.eq(0);
var allDoctorsContainer = allDoctors.find('.js-favorite-doctor-row-container');
if ( index > -1 ){
groupToRemove.find('.js-favorite-doctor-row').appendTo(allDoctorsContainer);
clickedTag.remove();
groupToRemove.remove();
this.updateSectionDropdowns();
this.ariaAlert('Group ' + removedGroupName + ' removed');
this.hideConfirm(evt);
}
}
};
this.showAlert(evt, deleteGroup);
},
showAlert: function (evt, callback) {
that = this;
var clickedTag = '';
clickedTag = $(evt.currentTarget).closest('.tag');
clickedTag.addClass('is-active').attr('data-delete','true');
$('.delete-acct-message').show().focus();
$('.js-remove-yes').on('click', function(evt){
evt.preventDefault();
callback.apply(that);
});
$('.js-remove-no').on('click', function(evt){
evt.preventDefault();
this.hideConfirm(evt);
});
},
I would suggest that you should use custom attributes in your html, this will simplify your javascript logic and make it more effective and efficient.
I have modified your html and javascript to add the support for custom attribute data-doc-group. Have a look at your group sections div here
<div data-doc-group="lauren" class="section-border-top--grey js-favorite-group">
<h4 class="expandable__cta cta--std-teal js-expand-trigger"><span class="icon icon-plus--teal expandable__cta-icon"></span>Lauren's Doctors</h4>
<div class="expandable__content js-favorite-doctor-row-container" aria-expanded="true">
<div class="location-section dr-profile">
<div class="section__content js-doctor-row">
<div class="favorite-doctor-manage__row">
DR info
</div>
</div><!--/section__content-->
</div><!--/location-section-->
</div>
Here are the tags with custom attributes
<li data-doc-group="lauren" class="tag tag--teal">
Lauren's Doctors
<ul class="tag-sub">
<li><button class="tag-icon tag-icon--close-white js-group-remove">Remove group: Lauren's Doctors</button></li>
</ul>
</li>
<li data-doc-group="timmy" class="tag tag--teal">
Timmy's Doctors
<ul class="tag-sub">
<li><button class="tag-icon tag-icon--close-white js-group-remove">Remove group: Timmy's Doctors</button></li>
</ul>
</li>
Here is the javascript to handle this, (this may be a bit buggy, but will give you a general idea)
removeGroup: function(evt) {
this.showAlert(evt, function() {
var $clickedTag = $(evt.currentTarget).closest('.tag'),
dataGroupName,
$groupToRemove,
removedGroupName,
$allDoctors = $('.js-favorite-group').eq(0),
$allDoctorsContainer = $allDoctors.find('.js-favorite-doctor-row-container');
if ($clickedTag.hasClass('is-active')){
dataGroupName = $clickedTag.data('doc-group');
$groupToRemove = $allDoctors.siblings('[data-doc-group="' + docGroupName + '"]');
if ($groupToRemove.length > 0){
$groupToRemove.find('.js-favorite-doctor-row').appendTo($allDoctorsContainer);
$clickedTag.remove();
$groupToRemove.remove();
removedGroupName = this.getGroupNameForSection($groupToRemove);
this.updateSectionDropdowns();
this.ariaAlert('Group ' + removedGroupName + ' removed');
this.hideConfirm(evt);
}
}
});
}

KNOCKOUT JS observableArray duplicate

My html page is :
<div data-role="content">
<div id="menu">
<ul id="menu" data-role="listview" class="ui-listview "
data-bind="foreach: menu">
<li>
<a data-bind="text:name, attr: {href: urlmenu}"></a>
<a href="#" data-bind="{ click: $parent.remove }"
data-role="button" data-icon="delete"></a>
</li>
</ul>
</div>
</div>
<div data-role="footer" data-position="fixed">
<div data-role="navbar">
<ul id="footer">
<li>Home</li>
<li>Asignaturas</li>
<li>Mensajes</li>
</ul>
</div>
</div>
And my JS code is :
$( document ).on( "pagebeforechange" , function(e, data) {
var toPage = data.toPage[0].id;
if( toPage == "home"){
ko.cleanNode(document.getElementById('menu'));
menu();
}
});
function menuViewModel(){
var self = this;
self.menu = ko.observableArray([]);
self.menu.removeAll();
self.menu = ko.observableArray([
new EditMenuViewModel("Perfil"),
new EditMenuViewModel("Asignaturas")
]);
}
function EditMenuViewModel(name) {
this.name = ko.observable(name);
this.urlmenu = ko.observable("#"+name);
};
function menu(){
var menuviewModel = new menuViewModel();
ko.applyBindings(menuviewModel, document.getElementById('menu'));
}
When I load my page for the first time everything works fine, but when I click on link footer home, the array content is duplicated.
Example is here:
Any idea?
Thanks
You have two DOM elements with id=menu, a div and a ul.
<div id="menu"> <!-- <-- change this id for example -->
<ul id="menu" data-role="listview" class="ui-listview "
data-bind="foreach: menu">
...
</ul>
</div>
Ids should be unique, you need to change the id on one of your elements, hopefully this will also solve your problem.
Update
As you can read in this thread, ko.cleanNode will not remove items created using foreach binding.
You need to change your approach.
Here is a jsFiddle that reproduces your problem.
What you can do is stop cleaning+applying bindings, and update your observableArray instead:
$( document ).on( "pagebeforechange" , function(e, data) {
var toPage = data.toPage[0].id;
if( toPage == "home"){
menuviewModel.menu.removeAll(); //clear menu
//add whatever menu item you need
menuviewModel.menu.push(new EditMenuViewModel("New Menu1 " + (new Date()).getTime()));
menuviewModel.menu.push(new EditMenuViewModel("New Menu2 " + (new Date()).getTime()));
}
});
function menuViewModel(){
var self = this;
self.menu = ko.observableArray([]);
self.menu.removeAll();
self.menu = ko.observableArray([
new EditMenuViewModel("Perfil"),
new EditMenuViewModel("Asignaturas")
]);
}
function EditMenuViewModel(name) {
this.name = ko.observable(name);
this.urlmenu = ko.observable("#"+name);
};
//bind only once
var menuviewModel = new menuViewModel();
ko.applyBindings(menuviewModel, document.getElementById('menu'));
Here is an example
This is old thread, but I've found a (ugly) way to overcome it:
before the cleaning, I'm caching the observable array values and set it with only 1 value. After the rebind, I'm restoring the cached values. Something like this:
var self = this;
self.myArray = ko.observableArray(['val1', 'val2']);
var tempArray = [];
self.BeforeCleaning = function () {
tempArray = self.myArray()
self.myArray(['temp value']);
};
self.AfterRebinding = function () {
self.myArray(tempArray);
};
horrible, but works for me.

How can I go to the Next and Previous value in a json array?

I've been beating my head against the wall with this one for days now. I have a list of items that when clicked, will move that indivual item to a div. Based on which item is clicked, I'm trying to go the the Next and Previous items in my list (json).
I'm kind of new to this and I just can't quite get this to work no matter what I try.
Here is the jsfiddle code. Thanks!
HTML:
<audio id="audio-player" name="audio-player" src="" ></audio>
<a id="next-bt" href="#">
<div class="player-musicnav-ff-column3">
<ul class="musicnav-ff">
<li class="ff">NEXT</li>
</ul>
</div>
</a>
<a id="prev-bt" href="#">
<div class="player-musicnav-ff-column3">
<ul class="musicnav-ff">
<li class="ff">PREV</li>
</ul>
</div>
</a>
<br/>
<br/>
<div id="player-digital-title">
</div>
<br/>
<br/>
<a href="#" id='player-handwriting-title'></a>​​
Javascript an Jquery:
$(document).ready(function(){
var treeObj = {"root":[{"id":"1","trackName":"Whippin Post"},{"id":"2","trackName":"Sweet Caroline"},{"id":"3","trackName":"Tears in Heaven"},{"id":"4","trackName":"Ain't She Sweet"},{"id":"5","trackName":"Octopus' Garden"},{"id":"6","trackName":"Teen Spirit"},{"id":"7","trackName":"Knockin on Heaven's Door"}]};
var $ul = $("<ul></ul>");
$.each(treeObj.root,function(i,v) {
$ul.append(
$("<li></li>").append( $("<a></a>")
.attr({"href":v.id,"data-file":v.trackFile})
.html(v.trackName)
)
);
});
$("#player-handwriting-title").empty().append($ul);
$("#player-handwriting-title a").click(function() {
var name = $(this).html(),
filename = $(this).attr("data-file");
filename2 = "upload-form/upload/" + filename;
$('#player-digital-title').html($(this).html());
document.getElementById('audio-player').src = filename2;
$("#audio-player")[0].play();
return false;
});
var myID = 0;
$("#next-bt").click(function() {
document.getElementById('player-digital-title').innerHTML = treeObj.root[++myID].trackName ;
});
var mylease = 6;
$("#prev-bt").click(function() {
document.getElementById('player-digital-title').innerHTML = treeObj.root[--mylease].trackName ;
});
});
​
Added a fiddle
http://jsfiddle.net/kabichill/JzL97/
check out..Hope its wat u expected...
try something like
var myID = 0;
$("#next-bt").click(function() {
myID++;
if(myID > treeObj.root.length) { myID = 0; }
document.getElementById('player-digital-title').innerHTML = treeObj.root[++myID].trackName ;
});
var mylease = 6;
$("#prev-bt").click(function() {
myID--;
if(myID < 0) { myID = treeObj.root.length; }
document.getElementById('player-digital-title').innerHTML = treeObj.root[--mylease].trackName ;
});

Categories

Resources