How can these generic classes be distinct between different divs? - javascript

I'm creating a notification system for a game to work similar to how notifications might work in a phone.
The notifications are all created initially, hidden, and later the game is supposed to "activate" certain ones from in-game triggers.
I'm running into problems when trying to keep the notifications separate in terms of their classes. Each notification starts off as a small rectangular box with only the title visible. Upon clicking, the notification expands and the description becomes visible.
Right now, clicking a notification does expand that notification and display its notification, but any other notifications also show their descriptions as well.
Example code:
var NotificationItems = new Array();
scope.registerNotification = function(title, description)
{
//add it to the array
NotificationItems.push(new scope.Application(title, description));
var $NotificationContainer = $("#NotificationContainer");
$NotificationContainer.append('<div class="Notification" title="'+title+'"></div>');
var $thisNotification = $NotificationContainer.children('.Notification[title='+title+']');
$thisNotification.append('<div class="NotificationTitle">'+title+'</div>');
$thisNotification.append('<div class="NotificationDescription">'+description+'</div>');
$(".NotificationDescription").hide();
$thisNotification.click(function()
{
$(this).toggleClass('expanded');
$('.NotificationDescription').slideToggle('slow');
});
}
How can I get the .NotificationDescription to be uniquely recognized for each notification?

You could try the .children() method: jQuery docs for children method
$thisNotification.click(function()
{
$(this).toggleClass('expanded').children('.NotificationDescription').slideToggle('slow');
});

Just find the right one for the clicked element:
$thisNotification.click(function()
{
$(this).toggleClass('expanded');
$(this).find('.NotificationDescription').slideToggle('slow');
});
You can chain the calls if you like:
$thisNotification.click(function()
{
$(this).toggleClass('expanded').find('.NotificationDescription').slideToggle('slow');
});

You might want to try out event delegations.
$('#NotificationContainer > div.Notification').live('click',function()
{
$(this).toggleClass('expanded').find('div.NotificationDescription').slideToggle('slow');
});
This way you only need to attach the event once (on init), and a single event handles all the notifications.
You also should add all your html at one time:
var $NotificationContainer = $("#NotificationContainer");
var $Notification = $('<div class="Notification" title="'+title+'"></div>');
$Notification.append('<div class="NotificationTitle">'+title+'</div>');
$Notification.append('<div class="NotificationDescription">'+description+'</div>');
$NotificationContainer.append($Notification);
notice the subtle difference, we're building the elements in jquery rather than the dom, and append them all at once.

Related

Why is my JavaScript code executing only on second click?

I have been toying with this forever now. I can't seem to find out why the lightbox is only executing after I've clicked it twice. The first click, it just pops the image up in a new tab.
I've already tried using e.preventDefault (which did nothing except keep the image from popping up in a new tab after the first click).
$(document).ready(function() {
$('a[class^="fomod-"]').on('click', function() {
var fomodClass = $(this).attr('class');
var fomodGallery = $('.' + fomodClass).simpleLightbox({
loop: false
});
});
});
What I'm ultimate trying to do is watch the DOM for any clicks on links that have the "fomod-*" class and, if clicked, get the exact class of the element that was clicked. With that, the lightbox pops up and only shows the other images with that same exact class as a gallery.
The Issue
.simpleLightbox() initializes the lightbox. That means your first click adds simpleLightbox to your page, allowing all following clicks to actually trigger it.
You need to do the initialization when the page loads. Now you could do something like...
$('a[class^="fomod-"]').each(function() { ... })
But that has a few drawbacks.
It won't find elements where fomod isn't the first class, ie class="other-class fomod-one".
If you had class="fomod-one other-class", your internal selector won't work because the concatenation would result in $(".fomod-one other-class").
You'd be continuously re-initializing simpleLightbox on the same elements repeatedly, which I'm not sure if the plugin is setup to handle or not.
Solution 1 - Data Attributes
data attributes allow us a bit more flexibility on how we select our elements. Also, fetching data attributes in JavaScript is supported in both vanilla JS (using dataset) and jQuery (using .data()).
<a data-fomod="Gallery1"></a>
<a data-fomod="Gallery1"></a>
<a data-fomod="Gallery2"></a>
$(document).ready(function() {
const galleries = $("[data-fomod]")
//Get array from elements
.get()
//Attach lightbox to each unique gallery on the page
.reduce((output, {dataset}) => {
let galleryName = dataset.fomod;
return output[galleryName]
? output
: {...output, [galleryName]: $(`[data-fomod="${galleryName}"]`).simpleLightbox({loop: false})}
}, {});
});
This approach gives us three things over the initial approach:
It doesn't restrict the use of classes.
It attaches simpleLightbox to each gallery just once.
It stores the galleries individually by name in the galleries object. For example, if you wanted to tell Gallery1 to go to the next slide, you could do galleries["Gallery1"].next().
Solution 2 - Using Classes More Appropriately
As you'd mentioned in the comments, your environment doesn't provide great support for data- attributes. Instead, we can use classes, we just have to be a bit more considerate. I'll use two classes here - one to flag this as a lightbox element ("fomod"), and another to associate the gallery ("fomod-GalleryName").
You may be wondering why that "flag" fomod class is necessary. Why not just use fomod- and use the ^= selector? As mentioned above, what if fomod- is the second class behind my-other-class? The selector won't find the element.
(There are ways around this but that's opening a can of worms.)
This approach, although just slightly more involved, achieves all the same benefits that the data attribute solution does.
<a class="fomod fomod-Gallery1"></a>
<a class="fomod fomod-Gallery1"></a>
<a class="fomod fomod-Gallery2"></a>
Without comments
$(document).ready(function() {
const galleries = $(".fomod")
.get()
.reduce((output, elem) => {
let galleryName = [...elem.classList].find(c => c.startsWith('fomod-'));
if (!galleryName) return;
galleryName = galleryName.split("-")[1];
return output[galleryName]
? output
: { ...output, [galleryName]: $(`.fomod-${galleryName}`).simpleLightbox({loop: false})}
}, {});
});
With comments
$(document).ready(function() {
const galleries = $(".fomod")
//Get array from elements
.get()
//For each fomod element...
.reduce((output, elem) => {
//Get the classes on this element, and find one that starts with "fomod-"
let galleryName = [...elem.classList].find(c => c.startsWith('fomod-'));
//Didn't find one. Skip this element
if (!galleryName) return;
//Remove the "fomod-" part so we're left with just the gallery name
galleryName = galleryName.split("-")[1];
//Have we already initialized this gallery?
return output[galleryName]
//Yup. Do nothing.
? output
//Nope. Initialize it now.
: { ...output, [galleryName]: $(`.fomod-${galleryName}`).simpleLightbox({loop: false})}
}, {});
});

Click one of contact in contact list in whatsapp web (javascript)

I try to use JavaScript to perform a click action to click a contact form contact list in order to open a chat in Whatsapp Web.
I use normal click action to click but not working, like
var div = document.querySelector('.infinite-list-item')[0];
div.click();
And i know whatsapp web is made by react js, ant special to perform a click?
I even use jQuery click() function but still not working, what should I use?
You're using document.querySelector() and then trying to access it as if it was a data structure.
document.querySelector() will return only one DOM element. You should try using document.querySelectorAll()[0] if you want to put all the contacts in a structure.
Solution:
var itemList = document.querySelectorAll('.infinite-list-item');
itemList[0].click();
Also, this would work as an alternative solution ( though using querySelector instead of querySelectorAll to target a class name that is used more than once is not at all recommended ):
var div = document.querySelector('.infinite-list-item');
div.click();
document.queryselectorAll("div span").forEach((/*YourElement*/, index) =>{
setTimeout(()=>{
/*here should go a validation so that it only runs on the
contacts you want, but i don't write it because that's not the
main idea*/
//¡¡¡¡the important part is this!!!!!
function simulateMouseEvents (elemento, eventName) {
var mouseEvent = document.createEvent ('MouseEvents');
mouseEvent.initEvent (eventName, true, true);
/*YourElement*/.dispatchEvent (mouseEvent);
}
simulateMouseEvents ('enlace', "mousedown");
},index*20)
}
/this code snippet solved my problem in a personal project ... I am enclosing image of the project(unfinished so it may have bugs) in case it helps you understand better/
Image of the proyect

Adding a class while looping to an array

I have a list composed by some divs, all of them have a info link with the class .lnkInfo. When clicked it should trigger a function that adds the class show to another div (like some sort of PopUp) so it is visible and when clicked again it should hide it.
I am quite certain this must be a very basic thing and most likely I will get some scoffs...but hey! Once I have this down that's one thing less I will ever have to ask again. Anyway I am starting to leave the safety of html and css to start learning JS, PHP and the like and I came to a bit of a problem.
When testing it before it was working, that was until I added another div, it only worked with the first one, reading a bit and with some suggestion I realized it must be something related to a array, the problem is that I am not quite certain of the syntax for accomplishing what I am visualizing.
Any help would be deeply appreciated.
This is my JS code and below I will attack a Fiddle of how the html looks just in case.
var infoLab = document.getElementsByClassName('lnkInfo'),
closeInfo = document.getElementById('btnCerrar');
infoLab.addEventListener('click', function () {
for (var i = 0 ; i < infoLab.length; i++) {
var links = infoLab[i];
displayPopUp('popUpCorrecto1', 'infoLab[i]');
};
});
function displayPopUp(pIdDiv, infoLab[i]){
var display = document.getElementById(pIdDiv),
for (var i = 0 ; i < infoLab.length; i++) {
infoLab[i]
newClass ='';
newClass = display.className.replace('hide','');
display.className = newClass + ' show';
};
}
JSFiddle.
Thanks a lot in advance and sorry for any facepalms!
EDIT:
This a jQuery function (in another file) that I need to call using the link because it fetches the data that will be inside the div, thus why I wanted to just add a hide/show.
$(".lnkInfo").click(function() {
var id = $('#txtId').val();
var request = $.ajax({
url: "includes/functionsLabs.php",
type: "post",
data: {
'call': 'displayInfoLabs',
'pId':id},
dataType: 'html',
success: function(response){
$('#info').html(response);
}
});
});
EDIT 2:
To a future reader of this question,
If you managed to find this answer throughout space and time, know that this is how the solution ended being, may it help you in your quest to stop being a noob.
SOLUTION
Here is a rudimentary working example of how to make a popup appear after clicking on a specific element given your current code. Note that I added an id to your link element.
// Select the element.
var infoLink1 = document.getElementById('infoLink1');
// Add an event listener to that element.
infoLink1.addEventListener('click', function () {
displayPopUp('popUpCorrecto1');
});
// Display a the popup by removing it's default "hide"
// class and adding a "show" class.
function displayPopUp(pIdDiv) {
var display = document.getElementById(pIdDiv);
var newClass = display.className.replace('hide', '');
display.className = newClass + ' show';
}
Fiddle.
There are various ways to generalize this to work for all links/popups. You could add a data-link-number=1, data-link-number=2, etc to each link element (more on data-). Select an element containing all of your links. Bind to that element an event listener that, when clicked, detects the link element that was clicked (see event delegation / "bubbling"). You can determine which link was clicked based on the value of your data-link-number attribute. Then show the appropriate popup.
You may also want to use jQuery for this. Changing an element's class by setting it's className property makes for brittle DOM code. There is an addClass and a removeClass method available. jQuery's events also work cross-browser; element.addEventListener() will not work in IE8 which still has a significant market share.

Windows 8 Grid Template JS App, CSS manipulations dont show on all selected items in groupedItems view

I'm using the Win8 Grid View Template to display infos from a news site. In the lower menu bar i have implemented a function wich shuts off the titles, so that only the pictures are still visible.
This function is in a "global.js" file which is included in the "default.html" so it's available everywhere and it looks like this:
//function to turn titles off and on
function titleToggle() {
var titles = document.getElementsByClassName("item-overlay");
for (var i = 0; i < titles.length; i++) {
if (Global.titlesAreOn) {
titles[i].style.display = "none";
}
else {
titles[i].style.display = "";
}
}
Global.titlesAreOn = !Global.titlesAreOn;
};
So when i call this function from the menu bar it works for the first items, but when i scroll the end of the groupedItems view (hubview) the titles are still there. When i then scroll back to the beginning the titles are there again too.
I'm also calling the titleToggle function from the ready() function of the "groupedItems.js" to check whether or not to display the titles depending on a global variable. When i do that (whenever i come back to the hubpage) it works all the way, just as expected.
ui.Pages.define("/pages/groupedItems/groupedItems.html", {
navigateToGroup: function (key) {
nav.navigate("/pages/groupDetail/groupDetail.html", { groupKey: key });
},
ready: function (element, options) {
appbar.winControl.disabled = false;
appbar.winControl.hideCommands(["fontSizeBt"]);
appbar.winControl.showCommands(["titleToggle"]);
if (Global.titlesAreOn == false) {
Global.titlesAreOn = true;
Global.titleToggle();
}
I made a short video to show the problem, because its kinda hard to explain --> http://youtu.be/h4FpQf1fRBY I hope you get the idea?
Why does it work when i call it from the ready() function?
Does anyone have an idea? Is it some kind of automatic item caching in order to have better performance? And how could this be solved?
Greets and thanks!
First, here is why this might be happening - WinJS is using single page navigation for the app experience. This means that when you navigate to a new page, actually you don't. Instead the content is removed from the page and the new content is loaded in the same page. It is possible that at the moment you press the button not all elements have been loaded in the DOM and therefore they cannot be manipulated by your function. This is why when you call it from the ready() function it works - all contents are loaded in the DOM. It is generally better to do things in the ready() function.
About the behavior when you slide back left and the items are again reloaded with titles - for some reason the listView items are reloading. Maybe you are using live data from the news site and they are refreshing with the listView control's template again. I cannot know, but it doesn't matter. Hiding the elements is not the best approach I think. It is better to have two templates - one with a title element and one without. The button click handler should get the listView controls(they have to be loaded) and change their templates.
ready: function (element, options) {
var button = document.getElementById('btn');
button.addEventListener("click", btnClickHandler);
}
And the handler:
function btnClickHandler(e) {
var listView = document.getElementById("listView").winControl;
var template2 = document.getElementById("template2");
listView.itemTemplate = template2;
};

Sencha Touch: update view during function

Here is what should happen:
I have a button with a label and an icon.
When I tap the button some actions will take place which will take some time. Therefore I want to replace the icon of the button with some loading-icon during the processing.
Normal Icon:
Icon replaced by loading gif:
So in pseudo code it would be:
fancyFunction(){
replaceIconWithLoadingIcon();
doFancyStuff();
restoreOldIcon();
}
However the screen isn't updated during the execution of the function. Here ist my code:
onTapButton: function(view, index, target, record, event){
var indexArray = new Array();
var temp = record.data.photo_url;
record.data.photo_url = "img/loading_icon.gif";
alert('test1');
/*
* Do magic stuff
*/
}
The icon will be replaced using the above code, but not until the function has terminated. Meaning, when the alert('1') appears, the icon is not yet replaced.
I already tried the solution suggested here without success.
I also tried view.hide() followed by view.show() but these commands weren't executed until the function terminated, too.
Let me know if you need further information. Any suggestions would be far more than welcome.
I finally found a solution displaying the mask during my actions are performed. The key to my solution was on this website.
In my controller I did the following:
showLoadingScreen: function(){
Ext.Viewport.setMasked({
xtype: 'loadmask',
message: 'Loading...'
});
},
onTapButton: function(view, index, target, record, event){
//Show loading mask
setTimeout(function(){this.showLoadingScreen();}.bind(this),1);
// Do some magic
setTimeout(function(){this.doFancyStuff(para,meter);}.bind(this),400);
// Remove loading screen
setTimeout(function(){Ext.Viewport.unmask();}.bind(this),400);
},
The replacing of the icons worked quite similar:
onTapButton: function(view, index, target, record, event){
//Replace the icon
record.data.photo_url = 'img/loading_icon.gif';
view.refresh();
// Do some magic
setTimeout(function(){this.doFancyStuff(para,meter);}.bind(this),400);
},
doFancyStuff: function(para, meter){
/*
* fancy stuff
*/
var index = store.find('id',i+1);
var element = store.getAt(index);
element.set('photo_url',img);
}
Thank you for your help Barrett and sha!
I think the main problem here is that your execution task is executing in the main UI thread. In order to let UI thread do animation you need to push your doFancyStuff() function into something like http://docs.sencha.com/touch/2.2.1/#!/api/Ext.util.DelayedTask
Keep in mind though, that you would need to revert it your icon only after fancy stuff is complete.
To update any button attributes you shoudl try to access the button itself. Either with a ComponentQuery or through the controllers getter. For Example:
var button = Ext.ComponentQuery.query('button[name=YOURBUTTONNAME]')[0];
button.setIcon('img/loading_icon.gif');
that shold update your button's icon.
also when you get a ref to the button you will have access to all the methods availble to an Ext.Button object:
http://docs.sencha.com/touch/2.2.1/#!/api/Ext.Button-method-setIcon

Categories