Site navigation catching events and triggering the right actions - javascript

I have dabbled with jQuery and javascript (I'm afraid in that order) before but allways very simple stuff, like sliding elements up, adding/removing classes etc.
I've not worked with events and objects before.
Now I have an allready working intranet "app" that is programmed exactly how I would do it. Namely lots of (if not all) global functions and variables to show / hide & generally control the page elements. all this is controlled without a mouse, only by keys.
Seeing this (and getting slightly scared in the process) I got myself a copy of "Object-Oriented JavaScript" By Stoyan Stefanov ...
My problem now is that I understand I'm going to need to work with objects, prototypes and events...
I -think I- understand the concepts by themselves but am totally at a loss at how to start.
Another problem I have is that the same keys should trigger different actions, depending on the visible or active elements. up/right/down/left should control the menus but when a content is loaded(end of menu is reached) the same keys have to trigger completely different things. I think I need to work with event-namespaces here, right?
Since it is not the mouse that is triggering the events I have to catch them at a very high level and determine what actions have to be made depending on the active and visible elements. Since I dont really get to know with event.target which element triggered the event
for example: if menu1 is visible and "right" was pressed
either open sub-menu or load content.
I have made a few attempts but was not able to get away from letting everything stay globally:
jQuery('body').on('ok.menu.rc right.menu.rc',function(e){
var cur = jQuery('nav.active a.active').removeClass('active').addClass('selected');
jQuery('nav.active a:not(.selected)').addClass('disabled').parent().removeClass('active');
//cur.parent().removeClass('active');
get_entry(cur.attr('data-name')+'-');
//jQuery('body').off('.menu'); // testing namespaces
jQuery('#menu1wrap nav').removeAttr('class').addClass('active');
});
And still nowhere near any kind of reuse-ability. It probably does not seem like it, but I have made several (and at least 2 different) attempts but grew frustrated pretty quickly.
Especially when trying out to put everything into objects. I wouldn't know how to link the html-elements with the instances of the javascript objects.
How would you take this beast on?

Related

Convenient way to get input for puppeteer page.click()

Challenge
When using puppeteer page.click('something') one of the first challenges is to make sure that the right 'something' is provided.
I guess this is a very common challenge, yet I did not find any simple way to achieve this.
What I tried so far
In Google Chrome I inspect the element that I want to click. I then get an extensive element description with a class and such. Based on an example I found, my approach is now:
Take the class
Replace all spaces with dots
Try
If it fails, check what is around this and add it as a prefix, for example one or two instances of button.
This does not exactly feel like it is the best way (and sometimes also fails, perhaps due to inaccuracies from my side).
One thing that I notice is that Chrome actually often seems to give a hint hovering over the thing I want to click, I am not sure if that is right but I also did not see a way to copy that (and it can be quite long).
If there is a totally different recommended way (e.g. Looking in the browser for what the name roughly is, and then using puppeteer to list all possible things), that is also fine. I just want to get the right input for page.click()
If you need an example of what I am trying: If you open this question in an incognito tab, you get options like share or follow. Or if you go to a web shop like staples and want to add something to cart.
When using puppeteer page.click('something') one of the first challenges is to make sure that the right 'something' is provided.
Just to be clear, "something" is a CSS selector, so your question seems to reduce to how to write CSS selectors that are accurate. Or, since Puppeteer offers XPath and traditional DOM traversals, we could extend it to include those selection tools as well.
Broader still, if there's a data goal we're interested in, often times there are other routes to get the data that don't involve touching the document at all.
I guess this is a very common challenge, yet I did not find any simple way to achieve this.
That's because there is no simple way to achieve this. It's like asking for the one baseball swing that hits all pitches. Web pages have messy, complex, arbitrary structures that follow thousands of different conventions (or no conventions at all). They can serve up a slightly or completely different page structure on any request. There's no silver-bullet strategy for writing good CSS selectors, and no step-by-step algorithm you can apply to universally "solve" the problem of accurately and robustly selecting elements.
Your goal should be to learn the toolkit and then practice on many different pages to develop an intuition for which tools and tricks work in which contexts and be able to correctly discern the tradeoffs in different approaches. Writing a full guide to this is out of scope, and articles exist elsewhere that cover this in depth, but here are a few high-level rules of thumb:
Look at context: consider the goals of your project, the general structure of the page and patterns on the page. Too many questions on Stack Overflow regarding CSS selectors (but also in general) omit context, which severely constrains the recommendation space, often leading to an XY problem. A few factors that are often relevant:
Whether the scrape is intended to be one-off or a long-running script that should try to anticipate and be resillient to page changes over time
Development time/cost/goal tradeoffs
Whether the data can be obtained by other means than the DOM, like accessing an API, pulling a JSON blob from a <script> tag, accessing a global variable on the window or intercepting a network response.
Considering nesting: is the element in a frame or shadow DOM?
Considering whole-page context: which patterns does the site tend to follow? Are there parent elements that are useful to selecting a child? (often, this is a distant relationship, not visible in a screenshot as provided by OP)
Consider all capabilities provided by your toolkit. For example, OP asked for a selector to close a modal on Stack Overflow; it turns out that none of the elements have particularly great CSS selectors, so using Puppeteer to trigger an Esc key press might be more robust.
Keep it simple: since pages can change at any time, the more constraints you add to the selector, the more likely one of those assumptions will no longer be true, introducing unnecessary points of failure.
Look for unique identifiers first: ids are usually unique on a page (some Google pages seem to scoff at this rule), so those are usually the best bets. For elements without an id, my next steps are typically:
Look for an id in a close parent element and use that, then select the child based on its next-most-unique identifier, usually a class name or combination tag name and attribute (like an input field with a name attribute, for example).
If there are few ids or none nearby, check whether the class name or attribute that is unique. If so, consider using that, likely coupled with a parent container class.
When selecting between class names, pay attention to those that seem temporary or stateful and might be added and removed dynamically. For example, a class of .highlighted-tab might disappear when the element isn't highlighted.
Prefer "bespoke" class names that seem tied to role or logic over generic library class names associated with styling (bootstrap, semantic UI, material UI, tailwind, etc).
Avoid the > operator which can be too rigid, unless you need precision to disambiguate a tree where no other identifiers are available.
Avoid sibling selectors unless unavoidable. Siblings often have more tenuous relationships than parents and children.
Avoid nth-child and nth-of type to the extent possibe. Lists are often reordered or may have fewer or more elements than you expect.
When using anything related to text, generally trim whitespace, ignore case and special characters where appropriate and prefer substrings over exact equality. On the other hand, don't be too loose. Usually, text content and values are weak targets but sometimes necessary.
Avoid pointless steps in a selector, like body > div#container > p > .target which should just be #container .target or #container p .target. body says almost nothing, > is too rigid, div isn't necessary since we have an id (if it changes to a span our new selector will still work), and the p is generic--there are probably no .targets outside of ps anyway.
Avoid browser-generated selectors. These are usually the worst of both worlds: highly vague and rigid at the same time. The goal is to be the opposite: accurate and specific, yet as flexible as possible.
Feel free to break rules as appropriate.

Disposing objects and restoring them

I am working on a three.js project that allows the user to switch through scenes that hold different mesh objects. They're all loaded into the project at the same time when the page has loaded, and all of these objects have textures which weigh heavily on the performance.
So to fix this, I want to temporarily remove all the objects in all of the scenes that the user has not selected. So, with 10 scenes, should the user for example select to see what's in scene 4 (which would be the currentScene), then scene 1 through 3 and 5 through 10 should have no objects that put an unnecessary strain on the performance. This should account as well for when the page has loaded. Only when the user selects one of those aforementioned scenes again, then it restores the scenes' objects.
This FIDDLE is an example of my project that encompasses the essential code. I am using the remove() and dispose() methods to achieve it.
//scene that's to be removed and disposed of
var destroyScenes = scenes[1];
//removes and disposes all objects in above scene
for (i = destroyScenes.children.length - 1; i >= 0; i--) {
obj = destroyScenes.children[i];
destroyScenes.remove(obj);
obj.geometry.dispose();
obj.material.dispose();
obj.texture.
}
My concrete question(s) is this:
How do I restore the objects if I remove and dispose of them? Is that a good approach to it, is it even possible, and/or is there a better way of achieving this?
Also, if that is a way to do it, how do I select all of the scenes except for the currentScene?
PS: I'm well aware that it'll put a loading time switching between scenes, but I'd still much more prefer that.
How do I restore the objects if I remove and dispose of them?
- If you dispose object right, you must load it again, which is ugly
Is that a good approach to it, is it even possible, and/or is there a better way of achieving this?
- If you plan to show object again, it is better to set obj.visible = false; Loading and disposing again is the worst performance method. Some things (as is fog etc..) cannot be applied correctly after loading object later.
Also, if that is a way to do it, how do I select all of the scenes except for the currentScene?
- Not sure about question, make a new question for that. (by controlling mouse move i guess)
I'm well aware that it'll put a loading time switching between scenes, but I'd still much more prefer that.
- It's not a way how it works. Scenes are running at the same time, you can stop animation frame for inactive scenes. But scenes will load on initialisation, not during switching them.
Ask always only one question pls.

List with 3500+ items is loaded via ajax a small bunch at a time. Stops updating in the UI, but I see the li tags being appended in DOM

I have a use case where there are thousands of items on a list shown to the user. They are loaded a small batch at a time and I see the network traffic going in and out, I see data getting loaded. I see the DOM getting bigger, but the list itself in the UI stops updating (Chrome).
When I examine it, I see thousands of items in the code, when I select the items through console and make it count them, I see the proper number. But in the page itself, I don't see these items get displayed. The list uses drag-and-drop to put items from it into another list (and load additional data about them).
Not using jquery.datatables at the moment, but been meaning to migrate to them a long time ago. I can’t find a good example, though, everything I see uses pagination to split, but what if this is not an option?
How can I pinpoint what it is that is preventing the items from display? The number of entries will vary between 500 and 20 000.
Never mind. everything works as intended, duh. I was stupid and missed something very obvious: things had "display: none" for a very good reason about which I totally forgot (has to do with the core logic of the application). Next time hit me with a stick so I could remember to pay more attention.
Not sure what you meant by saying 'DOM getting bigger' but 'don't see items get displayed'.
Typically JS has a main thread which will handle functions/callbacks as well as view-refresh. So if you operation is blocking , the view will not be refreshing.
As for the pagination is not an option thing, you can consider using DOM-lazy-Loading mechanism where you only put what should be in the current viewport into the dom. As user scroll, you calculate the scroll height dynamically to add/remove items to/from the DOM. One thing to remember is you typically need to define a fixed height for your rows so that you could do the calculation. This lazy-loading way is a common way of solving this type of problem and is widely used by different frameworks like GXT, angular-gird..etc.

How can I disable the "Up" arrow via CSS

I'm not sure this is even possible. I have a UI built that uses javascript and css, the interface users use a keypress to navigate. Css for the layouts that change per client, like a "skin". Javascript controls the functions and monitors the users keypresses.
The setup has 2 menus, a lower one and upper one. When you load it the lower one is focussed and you can use left or right arrows to go between the menu items.
In normal circumstances you would press "up" to get an upper menu, however this one client does not want this menu at all.
The problem is that we cannot just simply edit the javascript to prevent it, since the same javascript is used for all the clients, only different css get loaded based on which client is selected.
So now I have the upper menu hidden, but the problem is that the javascript still allows you to press up, but now since the top menu is not there, you lose your focus, and nothing is highlighted. The end user doesn't know why or how they lost focus if they press up by accident. (since they know no different setup where the top menu is there, they don't realize that they should press down, they just know nothing seems to happen when they press left/right)
What I want to do, is somehow, in the css, prevent the users from pressing up. Since we can't just go change the javascript.
Is this possible? Or is my only solution to modify the javascript?
TIA!!
Thanks to all who offer advise, just to clarify, we will make a new release with a function to check if the client wants this other menu, and disable the up button if not, however that will require a new release of the software, and a full pass through QA. Just trying to quick hack this one client in the mean time.. :)
Our final solution would have a boolean field in the database, and if it's true, the javascript will have an ignore for the up key, if false, then act normally.
Change the code so it only acts on up-arrow key-presses if an element isn't present.
EG add a p with the class="noupkey" then stop the event from firing like :
if($('.nokeyup').length === 0) {
//Do normal behaviour
}
Unfortunately there is no correct way to disable components through css, for this type of functionality is meant for javascript.
The philosophy for most programmers that I have worked with is that you should allow the end user to decide if they want to use a new feature.
What I mean by this, is that you should leave an option in the app that lets the person disable the component themselves. This way you can have the same set of javascript for all users and still disable any component in the correct way. The real advantage to this is that you wont have to deal with this ticket ever again, the support staff could simply walk any client through the operation of recreating the requested feature.
Most of this solution might be unusable to you however. I understand it's not easy changing your workplace convention.
Here is something that might be a little more useful, How do I disable form fields using CSS? Some of the advise might be applicable to any component, including menus.
Actually it is very odd solution but it can be done with little tricky solution .
Make One transparent Div with more z-index , with Absolute Position at the body level over that Up Button , For rest of your applications apply Pointer Events Css property to none so your mouse events will be done on the Below Div .
Then for your Case in which you want to disable that control do not give Pointer Events to the foremost div it will be disabled for mouse events .
Note : There is some issue for Pointer events in IE.

Bearing in mind accessibility, is it kosher to dynamically group LIs into DIVs?

Hi
To make convenient some animation, my strategy for a client-side script involves grouping list items into DIVs. It seems that having anything other than LIs as the children of OL or UL elements does validate. However, I'd be doing this in script, so no validation issue (for what that's worth!).
But might this cause a problem with screen readers and so on?
Thanks for any thoughts
One way to find out! I don't think there would be too much trouble, but the only way to know is to test it. Javascript's interaction with screen readers is a complex subject, so I'm hesitant to make any firm judgments. NVDA seems like a good place to start. :D
I'd normally steer clear of invalid markup, but I can't think of a better way to group things in the way you want to. Perhaps consider whether a single list is the best markup to use rather than multiple lists or some other set of constructs. Since you are needing to group list items in this way, it seems likely that there's a semantic reason for grouping them in markup as well.
As it goes, if browsers cope with the invalid markup in question, it is likely to work in screen readers and the like as well. As CrazyJugglerDrummer says, it's best to test it. However, you never know when that invalid markup is going to start causing problems in future software releases.
First off, screen readers and JavaScript do not have the best track record of compatibility. (Correct me if I'm wrong, I am no expert on usability) From what I have come to understand, screen readers typically read a static version of your page, and refresh it at seemingly random times irregardless of what dynamic changes have occured through JavaScript. This was my last experience with screen readers, but that was 3 years ago so things may have changed for the better.
To give an example, if your dynamic list is in fact an accordion that expands and contracts, you cannot with any degree of certainty know if a screen reader will display the text being shown (even if the JavaScript made the text visible to the screen).
Before I would start worrying about problems your script might cause for screen readers, I would instead focus on your overall accessibility of the site structure (HTML+CSS). Are you making integral elements to your page keyboard focusable? Are you following WAI-ARIA guidelines? Are you using the correct technique to hide content (situation dependent) display:none, left:-9999px, or visibility:hidden?

Categories

Resources