Consider the following code, which adds a sticky header on scroll to a page where a certain ID "sticky-header" is present and window size is greater than 425
sticky : function() {
var stickyTools = $j('#sticky-header');
if (stickyTools.length > 0 && window.outerWidth > 425) {
var headerHeight = stickyTools.height();
var fixmeTop = stickyTools.offset().top + headerHeight - 50;
$j(window).scroll(function() {
var currentScroll = $j(window).scrollTop();
if (currentScroll >= fixmeTop) {
stickyTools.addClass('sticky');
} else {
stickyTools.removeClass('sticky');
}
});
}
}
};
Now this works fine on one template but on another template I want to re-use the same functionality but ONLY if a check box is selected. The checkboxes have the class of "item-checkbox". I have a unique ID on that page which is "template-list".
I am unsure of the best way to nest another IF statement within the current one (if (currentScroll >= fixmeTop)) or if there is a more clever way of doing this?
Related
I have a function that hides a div with the corresponding ID based on a radio button change, however, I would like to hide multiple items at once and as ID is unique I am not able to just hide them all. How would I set up a class that I can hide and how would I adjust this code below to make that work?
Any help greatly appreciated
function onChangePackage() {
const nodes = document.getElementsByClassName("baseClass");
var selectedValue;
// Get selected radio
for (var i = 0, length = nodes.length; i < length; i++) {
if (nodes[i].checked) {
selectedValue = nodes[i].value;
break;
}
}
// Showing all nodes first
const nodePostFix = ['A','B','C'];
nodePostFix.forEach( node => {
const currentElement = elementsToHide.item(i);
if (currentElement.hasClass("hidden" + selectedValue)) {
currentElement.style.display = "none";
} else {
currentElement.style.display = "block";
}
});
};
You can use data attributes for this purpose together with the attribute selectors. So you need just to add the data-hidden-for attributes to the required nodes and access them using document.querySelector() or document.querySelectorAll()
First give all the elements a base class name baseClass. You could just give them a class name like hidden and then in your code you could do something like below:
const elementsToHide = document.getElementsByClassName("baseClass");
for (var i = 0; i < elementsToHide.length; i++) {
const currentElement = elementsToHide.item(i);
if (currentElement.hasClass("hidden")) {
currentElement.style.display = "none";
} else {
currentElement.style.display = "block";
}
}
And on the click event of the radio button you could add this class I mentioned above to whichever ones you want to hide:
element.classList.add("hidden");
or
element.classList.remove("hidden");
I'm showing a set of pictures in a page. I use a GridView to show the pictures. However, when the user resizes the screen to make it narrow, I switch to a ListView.
The problem now is synchronizing the scroll position for the two lists. My approach to the solution is,
1. Get the first visible item of the first list.
2. Scroll the second list to that item using ScrollIntoView
However I'm unable to see any property in GridView/ListView that gives me the first information. Any ideas?
Also any other ways of doing this are appreciated.
That seems to be just about the way I would first try to do it. You can use the ItemsPanelRoot property of the GridView/ListView and get the Children of the panel, then use TransformToVisual().TransformPoint() relative to the list control on each child to find the first one that is visible.
The one gotcha I can think of is when ScrollIntoView() would scroll the item that was first in view port in one list to show as last in view in the other one. Maybe you could get the ScrollViewer from the template of the list control (e.g. by using VisualTreeHelper) and scroll to the beginning of the list first?
The most simple way to do it all might be to just scroll to the same relative offset in the list coming into view as the one going out. It might not be very precise, but it could work.
You could even do a nice animated transition of elements in one list into the elements in the other one.
*Update
I asked around and it seems like I forgot that the default panels in GridView and ListView - the ItemsWrapGrid and ItemsStackPanel contain a FirstVisibleIndex property that could be used to get the object and then call ScrollIntoView() on the list control, which in turns takes a ScrollIntoViewAlignment enum you can use to say you want the scrolled-to-item to be the first visible (aligned to the leading edge).
*Update 2
For ListViewBase - you can also use the ListViewPersistenceHelper to get and set relative offsets.
This upcoming update to WinRT XAML Toolkit might be helpful as it would allow you to simply call: gridView.SynchronizeScrollOffset(listView); or vice versa.
public static class ItemsControlExtensions
{
public static ScrollViewer GetScrollViewer(this ItemsControl itemsControl)
{
return itemsControl.GetFirstDescendantOfType<ScrollViewer>();
}
public static int GetFirstVisibleIndex(this ItemsControl itemsControl)
{
// First checking if no items source or an empty one is used
if (itemsControl.ItemsSource == null)
{
return -1;
}
var enumItemsSource = itemsControl.ItemsSource as IEnumerable;
if (enumItemsSource != null && !enumItemsSource.GetEnumerator().MoveNext())
{
return -1;
}
// Check if a modern panel is used as an items panel
var sourcePanel = itemsControl.ItemsPanelRoot;
if (sourcePanel == null)
{
throw new InvalidOperationException("Can't get first visible index from an ItemsControl with no ItemsPanel.");
}
var isp = sourcePanel as ItemsStackPanel;
if (isp != null)
{
return isp.FirstVisibleIndex;
}
var iwg = sourcePanel as ItemsWrapGrid;
if (iwg != null)
{
return iwg.FirstVisibleIndex;
}
// Check containers for first one in view
if (sourcePanel.Children.Count == 0)
{
return -1;
}
if (itemsControl.ActualWidth == 0)
{
throw new InvalidOperationException("Can't get first visible index from an ItemsControl that is not loaded or has zero size.");
}
for (int i = 0; i < sourcePanel.Children.Count; i++)
{
var container = (FrameworkElement)sourcePanel.Children[i];
var bounds = container.TransformToVisual(itemsControl).TransformBounds(new Rect(0, 0, container.ActualWidth, container.ActualHeight));
if (bounds.Left < itemsControl.ActualWidth &&
bounds.Top < itemsControl.ActualHeight &&
bounds.Right > 0 &&
bounds.Bottom > 0)
{
return itemsControl.IndexFromContainer(container);
}
}
throw new InvalidOperationException();
}
public static void SynchronizeScrollOffset(this ItemsControl targetItemsControl, ItemsControl sourceItemsControl, bool throwOnFail = false)
{
var firstVisibleIndex = sourceItemsControl.GetFirstVisibleIndex();
if (firstVisibleIndex == -1)
{
if (throwOnFail)
{
throw new InvalidOperationException();
}
return;
}
var targetListBox = targetItemsControl as ListBox;
if (targetListBox != null)
{
targetListBox.ScrollIntoView(sourceItemsControl.IndexFromContainer(sourceItemsControl.ContainerFromIndex(firstVisibleIndex)));
return;
}
var targetListViewBase = targetItemsControl as ListViewBase;
if (targetListViewBase != null)
{
targetListViewBase.ScrollIntoView(sourceItemsControl.IndexFromContainer(sourceItemsControl.ContainerFromIndex(firstVisibleIndex)), ScrollIntoViewAlignment.Leading);
return;
}
var scrollViewer = targetItemsControl.GetScrollViewer();
if (scrollViewer != null)
{
var container = (FrameworkElement) targetItemsControl.ContainerFromIndex(firstVisibleIndex);
var position = container.TransformToVisual(scrollViewer).TransformPoint(new Point());
scrollViewer.ChangeView(scrollViewer.HorizontalOffset + position.X, scrollViewer.VerticalOffset + position.Y, null);
}
}
}
My User has the following selection in his Gdoc.
Now from the sidebar he wants to to replace the selection he made on the document. The GAS question is if it is possible to do that at once, something like:
var selection = DocumentApp.getActiveDocument().getSelection()
selection.replace("newtext")
Or do I have to loop through selection.getRangeElements() in order to delete them (or replace them) and than in someway place the new text in that position?
Not, that's not possible (well, if it is, it's not documented).
You have to loop through the selected elements, mainly because the selection may take part of paragraphs, forcing you to manage that. i.e. deleting just the selected part. And for completed selected elements, you can just remove them entirely (like images).
Here's an implementation on how to do this (part of the Kaylan's Translate script modified by me to properly replace images and partially selected paragraphs.
function replaceSelection(newText) {
var selection = DocumentApp.getActiveDocument().getSelection();
if (selection) {
var elements = selection.getRangeElements();
var replace = true;
for (var i = 0; i < elements.length; i++) {
if (elements[i].isPartial()) {
var element = elements[i].getElement().asText();
var startIndex = elements[i].getStartOffset();
var endIndex = elements[i].getEndOffsetInclusive();
var text = element.getText().substring(startIndex, endIndex + 1);
element.deleteText(startIndex, endIndex);
if( replace ) {
element.insertText(startIndex, newText);
replace = false;
}
} else {
var element = elements[i].getElement();
if( replace && element.editAsText ) {
element.clear().asText().setText(newText);
replace = false;
} else {
if( replace && i === elements.length -1 ) {
var parent = element.getParent();
parent[parent.insertText ? 'insertText' : 'insertParagraph'](parent.getChildIndex(element), newText);
replace = false; //not really necessary since it's the last one
}
element.removeFromParent();
}
}
}
} else
throw "Hey, select something so I can replace!";
}
I'm making a memory-match game in which you flip two cards over in order to get them to match.
I'm doing it with simple if statements as shown below:
if(click == 2) //denotes two cards being clicked
{
if(flippedArray[1].src === flippedArray[0].src) // if click 1 == click 2 then refer to function 'delayMatch' which sets click 1 and 2 cards to not be displayed
{
window.setTimeout(function() { delayMatch() }, 500);
console.log("EQUAL");
score = +25000;
}
else
{
window.setTimeout(function() { delayNoMatch() }, 500); // if click 1 != click 2 then display card.png
console.log("NOT EQUAL");
score = -1999;
}
function delayMatch() //function for matching pairs
{
flippedArray[0].style = "display:none;";
flippedArray[1].style = "display:none;";
}
function delayNoMatch() //function for non-matching pairs
{
flippedArray[0].src = "card.png";
flippedArray[1].src = "card.png";
}
click = 0; // when clicked two cards set click back to zero
}
As you can see if two cards match they're set to display:none. What I'm trying to do is link to an "end game" html page once all 36 divs are set to display: none or I guess once the function delayMatch() has been called 18 times.
I'm completely at a loss as how I can do this.
my goal is something like this:
flippedArray[0] and flippedArray[1] is just a temporary array to check if the two cards currently in play are a match or not.
I was thinking something like:
endGameCounter =0;
endGameCounter++; //in the matching if-statement
then if(endGameCounter == 18)
{
location.href='link here'
}
You can use one variable for count inside the page.
If you are doing it in JSP, JSTL can help you here.
use to set a variable and just check the value time to time.
c:set var="COUNT" value="SOMETHING"/>
Try
function GameIsOver(){
for (var i = 0; i < allCards.length; i++) {
if(allCards[i].style.display === 'none')return false;
}
return true;
}
I am quite new to Openlayers and was wondering if there is a method or event that returns the zooming direction, e.g. onzoomin/onzoomout events. I am using sproutcore 1.0 and trying to modify a feature font according to the zooming level. I tried working with Rules but according to the application structure this does not work. Here is my sample event of what I want to do:
this.map.events.on({ "zoomend": function (e) {
var sub = 0;
if (ZOOMOUT){
sub = this.getZoom();
} else {
sub = this.getZoom() * -1;
}
var font = myFeature.layer.styleMap.styles['default'].defaultStyle.fontSize;
font = font + sub*10;
myFeature.layer.redraw();
}});
Found a workaround using geometry bounds which gives a good result:
this.map.events.on({ "zoomend": function (e) {
var width = myFeature.geometry.bounds.right - myFeature.geometry.bounds.left;
var div = 0;
if (this.getZoom() > 12) {
div = 4;
} else {
div = 6;
}
myFeature.layer.styleMap.styles['default'].defaultStyle.fontSize = (width/((15 - this.getZoom())+1)) / div).toString() + "px";
myFeature.layer.redraw();
}});