Angularjs: How to *retrieve* css property from element in directive? - css

I can set css properties on an element in a directive. But I cannot retrieve css properties on an element using the same method, it just returns an empty string.
i.e: var test = element.css("background-size"); //does not work!
What am I doing wrong? See my link handler in my directive below:
link: function($scope, element, attrs) {
//debugger;
//handler for close button:
//its the first child within the parent element:
$scope.closeBtn = angular.element(element.children()[0]);
//save the background image so we can toggle its visibility:
$scope.backgroundImg = element.css("background","url(../../a0DK0000003XvBYMA0/assets/images/tabbed_panel_bkgd.png) no-repeat") ;//className:
element.css("background-position","0px 35px");
element.css("background-size", "924px 580px");
//above I was able to set css properties, but why can't I retrieve css properties like this??:
var test = element.css("background-size");
$scope.closeBtn.bind('click',function(){
TweenLite.to(element, .75, {top:"635px",ease:Power2.easeOut,
onComplete:function(){
$scope.opened = false;
$scope.closeBtn.css('opacity',0);
} });
})
//hander to raise tab panel:
element.bind('click', function() {
if(!$scope.opened){
//debugger;
$scope.closeBtn.css('opacity',1);
TweenLite.to(element, .75, {top:"150px",ease:Power2.easeOut});
$scope.opened = true;
}
});
}

I took a step back from my question and realized if I am trying to retrieve css properties like used to do with JQuery then I am probably not applying a solution in the "angular way". My original problem is that I needed to store css properties so I coule re apply them later. So instead of that approach, I used the ng-class directive to toggle the classes so I would not have to store anything.
<html>
<body>
<tabbed-Panel ng-class="btmTabPanelClass" >
<div ng-show="opened" class="tabPanelCloseBtn"> </div>
<tabs>
<pane ng-repeat="pane in panes" heading="{{pane.title}}" active="pane.active">
<div class ="tabPanelContent" ng-include src="activeContent()"></div>
</pane>
</tabs>
</tabbed-Panel>
</div
</body>
</html>
angular.module('directives', ['baseModule','ui.bootstrap'])
.directive('tabbedPanel',['$animator',function($animator) {
//debugger;
return {
//scope:{},
restrict:"E",
//add controller to here
controller:function($scope){
//debugger;
$scope.bTabClicked = 0;
$scope.curTabIdx = 0;
$scope.opened = false;
$scope.closeBtn = null;
$scope.arClasses = ["bottomTabPanel", " bp_off"];
$scope.btmTabPanelClass = $scope.arClasses[0] + $scope.arClasses[1] ;
//get the tabs from the flows.json so we can create a model for the tab panel!
$scope.panes = $scope.flows[$scope.getCurFlowIdx()].array_data[$scope.getCurPageIdx()].tab_data;
//first tab is active by default:
//$scope.panes[0].active = true;
//set the content for the current tab:
$scope.activeContent = function() {
for (var i=0;i<$scope.panes.length;i++) {
if ($scope.panes[i].active) {
$scope.curTabIdx = i;
return $scope.panes[i].content;
}
}
};
//tab click watcher (to make sure user clicks on tab and not tab container):
$scope.$watch('activeContent()', function(paneIndex) {
++$scope.bTabClicked;
});
//--------------------------------------------------
},
link: function($scope, element, attrs) {
//debugger;
//handler for close button:
//its the first child within the parent element:
$scope.closeBtn = angular.element(element.children()[0]);
$scope.closeBtn.bind('click',function(){
// set all tabs to inactive:
$scope.bTabClicked = 0;
for (var i=0;i<$scope.panes.length;i++)
$scope.panes[i].active = false;
TweenLite.to(element, .75, {top:"635px",ease:Power2.easeOut,
onComplete:function(){
$scope.opened = false;
$scope.btmTabPanelClass = $scope.arClasses[0] + $scope.arClasses[1] ;
$scope.$apply(); //force binding to update
$scope.bTabClicked = 0;
} });
})
/*hander to raise tab panel:*/
element.bind('click', function() {
if(!$scope.opened && $scope.bTabClicked){
//debugger;
TweenLite.to(element, .75, {top:"150px",ease:Power2.easeOut});
$scope.opened = true;
$scope.btmTabPanelClass = $scope.arClasses[0] ;
$scope.$apply(); //force binding to update
}
else
$scope.bTabClicked = 0;
});
}
};
}]);

You can access the CSS style of an Angular element in a directive's link function by
var style = window.getComputedStyle(element[0]),
And then access the value of any CSS rule like such:
var color = style.getPropertyValue('color');

Related

How to update widget when new style is applied

I'm making an extension with a list containing a checkbox with a text item (St.label) that change style when toggled.
I'm listening to the toggle event, and as the item is toggled, I set a new style for my text using set_style_class_name() on a my Stlabel. But the style of the object don't change. The only solution that I have found is to destroy and remake all the item of the list and set a different class in the init of the object.
How could I just update the item that have been checked ?
Here the item that I'm using, I put a listener on the checkbox that trigger the toggle() function, in this function I'm updating the class, which should remove the 'text-checked' class and so the text should't have 'text-decoration:line-through' property.
const PopupMenu = imports.ui.popupMenu;
const Lang = imports.lang;
const { Atk, Clutter, St, GObject } = imports.gi;
const ExtensionUtils = imports.misc.extensionUtils;
const Me = ExtensionUtils.getCurrentExtension();
const CheckboxLib = Me.imports.src.checkbox;
var PopupCheckBoxMenuItem = GObject.registerClass({
Signals: {
'toggled': { param_types: [GObject.TYPE_BOOLEAN] },
'deleted': { param_types: [GObject.TYPE_BOOLEAN] }
},
}, class PopupCheckBoxMenuItem extends PopupMenu.PopupBaseMenuItem {
_init(text, active, params) {
super._init(params);
this.label = new St.Label({
text: text,
y_align:Clutter.ActorAlign.CENTER,
x_expand: true,
style_class: active ? "text-checked" : ""
});
this.tags = new St.Label({
text: "API",
y_align:Clutter.ActorAlign.CENTER,
style_class: "tag-item"
});
this.icon = new St.Button({
style_class: 'remove-task',
can_focus: true,
});
this.icon.connect('clicked', Lang.bind(this,function(){
this.emit('deleted', this._checkbox.state);
}));
this.icon.add_actor(new St.Icon({
icon_name: 'window-close-symbolic',
style_class: 'icon-remove-task'
}));
this._checkbox = new CheckboxLib.CheckBox(active);
this._checkbox.connect('clicked', Lang.bind(this,function(){
this.toggle();
}));
this.accessible_role = Atk.Role.CHECK_MENU_ITEM;
this.checkAccessibleState();
this._statusBin = new St.Bin({
x_align: Clutter.ActorAlign.START,
x_expand: false,
});
this.add_child(this._statusBin);
this.label_actor = this.label;
this.add_child(this.tags);
this.add_child(this.label);
this.add_child(this.icon);
this._statusLabel = new St.Label({
text: '',
style_class: 'popup-status-menu-item',
});
this._statusBin.child = this._checkbox;
}
setStatus(text) {
if (text != null) {
this._statusLabel.text = text;
this._statusBin.child = this._statusLabel;
this.reactive = false;
this.accessible_role = Atk.Role.MENU_ITEM;
} else {
this._statusBin.child = this._checkbox;
this.reactive = true;
this.accessible_role = Atk.Role.CHECK_MENU_ITEM;
}
this.checkAccessibleState();
}
activate(event) {
super.activate(event);
}
toggle() {
this._checkbox.toggle();
this.emit('toggled', this._checkbox.state);
//Updating class
this.label.set_style_class_name("new_class");
this.label.real_style_changed();
this.checkAccessibleState();
}
get state() {
return this._checkbox.state;
}
get delete_icon() {
return this.icon;
}
setToggleState(state) {
this._checkbox.state = state;
this.checkAccessibleState();
}
checkAccessibleState() {
switch (this.accessible_role) {
case Atk.Role.CHECK_MENU_ITEM:
if (this._checkbox.state)
this.add_accessible_state(Atk.StateType.CHECKED);
else
this.remove_accessible_state(Atk.StateType.CHECKED);
break;
default:
this.remove_accessible_state(Atk.StateType.CHECKED);
}
}
});
The problem was the property I changed in the css class.
For an unknown reason, style change doesn't seem to redraw when I set a class with only text-decoration property but if I add a change in the color, even if it is the same color it does work even without St.Widget.style_changed().
So if I do this.label.set_style_class_name("text-checked"); to change my class, the change doesn't work if my css class is as follow :
.text-checked
{
text-decoration: line-through !important;
}
But this work :
.text-checked
{
text-decoration: line-through !important;
color: black;
}
Must be an issue with how the style change event work for Gjs component.
Issue open here : https://gitlab.gnome.org/GNOME/gnome-shell/-/issues/2811
The most direct seems to be St.Widget.style_changed(). This seems to forcibly mark the style state as dirty and trigger a redraw (St.Label is a subclass, so just call myLabel.style_changed()).
The proper route is probably St.Widget.ensure_style(), though.
I didn't look too deep, but the issue may be that widgets aren't being marked as having their style changed or maybe the change isn't being propagated to children or something.

Drag and Drop in meteor

I am trying to use drag and drop on background image in a div but nothing is working. I did not find any drag and drop module for image in meteor. Is there any module or any default function in meteor to drag a background image. After uploading image is coming in div background now i want that user can drag that image and can set it's position. This is my code where i am showing image in background after uploading.
<div id="edit-image" class="text-center {{page}} {{isIosDevices}} {{profileHeader}}" style="{{myCoverPicture}}">
{{> uploaderbg profileHeader="profileHeader" userProfile=this.profile fromProfile=true}}
</div>
======= Interact JS ==================
'click .text-center': function (e) {
var isDraggable = interact('#test-img').draggable(); // true
}
<div id="my-image" class="text-center" style="">
<img src="{{myPicture}}" id="test-img" />
</div>
=================================================
Template.dragImgBg.onCreated(function helloOnCreated () {
const instance = this;
var ImageAxis1 = Meteor.user().profile.imageAxis;
values=ImageAxis1.split(' ');
instance.offsetx = new ReactiveVar(values[0]);
instance.offsety = new ReactiveVar(values[1]);
//console.log(ImageAxis1);
// fixed in this example
instance.bgUrl = new ReactiveVar(Meteor.user().profile.coverPicture);
})
Template.dragImgBg.helpers({
offsetx() {
return Template.instance().offsetx.get()
},
offsety() {
return Template.instance().offsety.get()
},
bgUrl() {
return Template.instance().bgUrl.get()
}
})
let active = false
Template.dragImgBg.events({
'mouseup' (/* event, templateInstance */) {
active = false
},
'mouseout .img-bg-movable' (/* event, templateInstance */) {
active = false
},
'mousedown .img-bg-movable' (/* event, templateInstance */) {
active = true
},
'mousemove'(event, templateInstance) {
if (!active) {
return
}
const movementx = event.originalEvent.movementX;
const movementy = event.originalEvent.movementY;
const oldx = templateInstance.offsetx.get();
const oldy = templateInstance.offsety.get();
let data = $('#data_img_pos')[0];
data.value = (oldx + movementx)+" "+(oldy + movementy);
templateInstance.offsetx.set(oldx + movementx);
templateInstance.offsety.set(oldy + movementy);
}
})
<template name="dragImgBg">
<div id="edit-image" class="img-bg-movable bg-img text-center {{page}} {{isIosDevices}}" style="background-position: {{offsetx}}px {{offsety}}px;background-image: url({{bgUrl}});">
{{> uploaderbg profileHeader="profileHeader" userProfile=this.profile fromProfile=true}}
</div>
</template>
After realizing, that this is not trivial in Blaze using third party libraries I tried to write some custom code.
Consider the following Template:
<template name="dragImgBg">
<div class="img-bg-movable" style="background-position: {{offsetx}}px {{offsety}}px;background-image: url({{bgUrl}});"></div>
</template>
with the following (examplatory) CSS:
.img-bg-movable {
width: 600px;
height: 250px;
overflow: hidden;
border: solid 1px #AAAAAA;
cursor: grab;
}
.img-bg-movable:active:hover {
cursor: grabbing;
}
As you can see the div is dynamically accepting styles, such as background image url (the one you get from your uploaded images) and x / y offset for the position.
The values for those styles are saved in reactive sources like a ReactiveVar and provided by simple helpers:
Template.dragImgBg.onCreated(function helloOnCreated () {
const instance = this
instance.offsetx = new ReactiveVar(0)
instance.offsety = new ReactiveVar(0)
// fixed in this example
instance.bgUrl = new ReactiveVar('https://upload.wikimedia.org/wikipedia/commons/3/3f/Caldwell_68_Corona_Australis_Dark_Molecular_Cloud.jpg')
})
Template.dragImgBg.helpers({
offsetx() {
return Template.instance().offsetx.get()
},
offsety() {
return Template.instance().offsety.get()
},
bgUrl() {
return Template.instance().bgUrl.get()
}
})
In order to change these values (and thus move the image) there needs to be some events that check, whether the element has been left-mouse-pressed and the mouse is moved.
If so, the delta values of the mouse-move are added to the reactive offset x / y sources. If the mouse is released or moved outside the image the values won't be applied.
let active = false
Template.dragImgBg.events({
'mouseup' (/* event, templateInstance */) {
active = false
},
'mouseout .img-bg-movable' (/* event, templateInstance */) {
active = false
},
'mousedown .img-bg-movable' (/* event, templateInstance */) {
active = true
},
'mousemove'(event, templateInstance) {
if (!active) {
return
}
const movementx = event.originalEvent.movementX
const movementy = event.originalEvent.movementY
const oldx = templateInstance.offsetx.get()
const oldy = templateInstance.offsety.get()
templateInstance.offsetx.set(oldx + movementx)
templateInstance.offsety.set(oldy + movementy)
}
})
The originalEevnt refers to the original event that is wrapped by the Template's jQuery event. You may customize the Template your needs.
If you know for example the dimensions of the image you could stop updating the position of offsetx or offsety reach these boundaries.
If you want to make this persistent (like for a user profile page) you can save the values of bgUrl (or the image file id of the uploaded image) and the offset x / y values in a collection and load these vlaues in onCreated 's autorun routine.

How to persist scrolls with turbolinks?

Sometimes it is desirable to persist scroll positions between page visits.
Turbolinks resets scroll position after loading the data.
How can I disable it for particular elements?
My solution in ES6:
const turbolinksPersistScroll = () => {
const persistScrollDataAttribute = 'turbolinks-persist-scroll'
let scrollPosition = null
let enabled = false
document.addEventListener('turbolinks:before-visit', (event) => {
if (enabled)
scrollPosition = window.scrollY
else
scrollPosition = null
enabled = false
})
document.addEventListener('turbolinks:load', (event) => {
const elements = document.querySelectorAll(`[data-${persistScrollDataAttribute}="true"]`)
for (let i = 0; i < elements.length; i++) {
elements[i].addEventListener('click', () => {
enabled = true
})
}
if (scrollPosition)
window.scrollTo(0, scrollPosition)
})
}
turbolinksPersistScroll()
And add on your links data-turbolinks-persist-scroll=true on links you want persist scrollbar position.
<a href="..." data-turbolinks-persist-scroll=true>Link</a>
This works for me, also with link_to remote: true.
Use the following javascript to persist scrolls. I have created a selector that matches all elements with class turbolinks-disable-scroll. Before loading,the script saves the scroll position and after loading it loads the saved positions.
// persist scrolls
// pirated from https://github.com/turbolinks/turbolinks-classic/issues/205
var elementsWithPersistentScrolls, persistentScrollsPositions;
elementsWithPersistentScrolls = ['.turbolinks-disable-scroll'];
persistentScrollsPositions = {};
$(document).on('turbolinks:before-visit', function() {
var i, len, results, selector;
persistentScrollsPositions = {};
results = [];
for (i = 0, len = elementsWithPersistentScrolls.length; i < len; i++) {
selector = elementsWithPersistentScrolls[i];
results.push(persistentScrollsPositions[selector] = $(selector).scrollTop());
}
return results;
});
$(document).on('turbolinks:load', function() {
var results, scrollTop, selector;
results = [];
for (selector in persistentScrollsPositions) {
scrollTop = persistentScrollsPositions[selector];
results.push($(selector).scrollTop(scrollTop));
}
return results;
});
It seems like there are two approaches to this problem.
Preserve flagged elements (#vedant1811's answer)
Preserve body scroll for flagged links
The second approach is the one that I've been looking for and couldn't find anywhere, so I'll provide my answer to that here.
The solution here is very similar to that of the first approach, but perhaps a little simpler. The idea is to grab the current scroll position of the body when an element is clicked, and then scroll to that position after the page is loaded:
Javascript
Turbolinks.scroll = {}
$(document).on('click', '[data-turbolinks-scroll=false]', function(e){
Turbolinks.scroll['top'] = $('body').scrollTop();
})
$(document).on('page:load', function() {
if (Turbolinks.scroll['top']) {
$('body').scrollTop(Turbolinks.scroll['top']);
}
Turbolinks.scroll = {};
});
Markup
<a href='/' data-turbolinks-scroll='false'>Scroll preserving link</a>
I use a scroll attribute on the Turbolinks object to store my scroll position when a [data-turbolinks-scroll=false] link is clicked, then after I scroll the page I clear this attribute.
It is important that you clear the attribute (Turbolinks.scroll = {}) otherwise, subsequent clicks on non-flagged anchor links will continue to scroll you to the same position.
Note: depending on the specific styling of html and body you may need to use the scroll offset from both. An example of how this might be accomplished is:
Turbolinks.scroll = {};
$(document).on('click', '[data-turbolinks-scroll=false]', function (e) {
Turbolinks.scroll['top'] = {
html: $("html").scrollTop(),
body: $("body").scrollTop()
}
});
$(document).on('turbolinks:load', function() {
if (Turbolinks.scroll['top']) {
$('html').scrollTop(Turbolinks.scroll['top']['html']);
$('body').scrollTop(Turbolinks.scroll['top']['body']);
}
Turbolinks.scroll = {};
});
I noticed that sometimes scroll is going up and then only down. This version prevents such behaviour:
const persistScrollDataAttribute = 'turbolinks-persist-scroll';
let scrollPosition = null;
const turbolinksPersistScroll = () => {
if (scrollPosition) {
window.scrollTo(0, scrollPosition);
scrollPosition = null;
}
const elements = document.querySelectorAll(`[data-${persistScrollDataAttribute}="true"]`)
for (let i = 0; i < elements.length; i++) {
elements[i].addEventListener('click', () => {
document.addEventListener("turbolinks:before-render", () => {
scrollPosition = window.scrollY;
}, {once: true})
})
}
}
document.addEventListener('turbolinks:load', turbolinksPersistScroll);
document.addEventListener('turbolinks:render', turbolinksPersistScroll);

how to trigger JQuery .draggable() on elements created by templates?

I have a standard template in an Html file like:
<template name="cards">
{{#each all_cards}}
{{> card_item}}
{{/each}}
</template>
<template name="card_item">
<div class="card" style="left:{{position.x}}px; top:{{position.y}}px">
{{title}}
</div>
</template>
I want to have the cards (css selector .card) become draggable with JQuery.
Now since Meteor automagically updates the DOM using the template, when and how do I know where to call .draggable() on what??
EDIT: This is so far my solution which makes pending movements on other client visible with a wobble animation (using CSS3):
Template.card_item.events = {
'mouseover .card': function (e) {
var $target = $(e.target);
var $cardContainer = $target.hasClass('card') ? $target : $target.parents('.card');
$cardContainer.draggable({containment: "parent", distance: 3});
},
'dragstart .card': function (e) {
Session.set("dragging_id", e.target.id);
$(e.target).addClass("drag");
pos = $(e.target).position();
Events.insert({type: "dragstart", id:e.target.id, left: pos.left, top: pos.top});
},
'dragstop .card': function (e) {
pos = $(e.target).position();
Events.insert({type: "dragstop", id:e.target.id, left: pos.left, top: pos.top});
Cards.update(e.target.id, {$set: {left:pos.left, top:pos.top}});
Session.set("dragging_id", null);
}
}
Events.find().observe({
added: function(event) {
if (event.type == "dragstart" && !Session.equals("dragging_id", event.id)) {
$("#"+event.id).draggable({disabled: true});
$("#"+event.id).addClass("wobble");
}
if (event.type == "dragstop" && !Session.equals("dragging_id", event.id)) {
$("#"+event.id).animate({left: event.left, top: event.top}, 250);
Events.remove({id:this.id});
$("#"+event.id).draggable({disabled: false});
}
}
});
EDIT: This approach doesn't seem to work in the latest versions of Meteor, e.g. v0.5.0. See my comment below.
Looks like we're working on similar things! I've got a working proof of concept for a simple Magic: The Gathering app. Here's how I have dragging implemented at the moment:
In a <head> section in one of your html files, include the jQuery UI script:
<script src="jquery-ui-1.8.20.custom.min.js"></script>
Then, in a js file, make sure elements become draggable on mouseover (note: this is sub-optimal on touchscreens since it requires two touches to drag... I'm looking for a better touchscreen solution):
Template.card_item.events['mouseover .card, touchstart .card'] = function (e) {
var $target = $(e.target);
if (!$target.data('isDraggable')) {
$target.data('isDraggable', true).draggable();
}
};
And finally, handle the drag and dragstop events:
var prevDraggedTime = 0
Template.card_item.events['drag .card'] = function (e) {
// get the cardId from e
var now = new Date().getTime();
var position;
if (now - prevDraggedTime > 250) {
position = $(e.target).position();
Cards.update(cardId, {$set: {x: position.top, y: position.left}});
prevDraggedTime = now;
}
}
Template.card_item.events['dragstop .card'] = function (e) {
// get the cardId from e
var position = $(e.target).position();
Cards.update(cardId, {$set: {x: position.top, y: position.left}});
}

how to concisely write this javascript to show/hide a list of elements?

How to write this type of code in loop? Actually I don't want to write the same same line again and again, Is their any way to compress this code? can we write this code in loop?
function showCandidates()
{document.getElementById("cand9").style.display="block";
document.getElementById("cand10").style.display="block";
document.getElementById("cand11").style.display="block";
document.getElementById("cand12").style.display="block";
document.getElementById("cand13").style.display="block";
document.getElementById("cand14").style.display="block";
document.getElementById("cand15").style.display="block";
document.getElementById("hide_cand").style.display="block";
document.getElementById("view_cand").style.display="none";
}
function hideCandidates()
{document.getElementById("cand9").style.display="none";
document.getElementById("cand10").style.display="none";
document.getElementById("cand11").style.display="none";
document.getElementById("cand12").style.display="none";
document.getElementById("cand13").style.display="none";
document.getElementById("cand14").style.display="none";
document.getElementById("cand15").style.display="none";
document.getElementById("hide_cand").style.display="none";
document.getElementById("view_cand").style.display="block";
}
I suggest this way:
var show_ids = ["cand9", "cand10", "cand11"] // ... and so on
funciton showCandidates() {
for (var index in show_ids) {
var id = show_ids[index];
document.getElementById(id).style.display="none";
}
}
similar for hideCandidates
You should assign to your html elements a class for example
<div class="hideable" >content </div>
Then either you use JQuery or plain javascript to get all the elements that have the "hideable class attribute:
document.getElementsByClassName('hideable')
or
>$(".hideable")
Since your the two previous methods will return an array, you will have to loop through the array and apply the appropriate style attribute.
Firstly, this can be all encapsulated into one function. The function can take a parameter to assign to the display property. And obviously use some if statement in there to deal with the view_cand elements' display.
I would look into using jquery for this though, it makes selecting DOM elements (especially sets of DOM elements) a damn site easier.
I'd write the code for you here but I don't know anything about the elements you're selecting or the structure to your DOM.
Something like this?
for(i=0;i<candNumber;i++){
id= "cand" + i;
document.getElementById(id).style.display="block";
}
Try this .It'll hide/show ( the wayas you requested) by parameter given to function.
setVisibilityByClass("visible"/"invisible") - shows/hides by changing class
setVisibility("block"/"none") - shows/hides by changing styles directly
CHOOSE ONLY ONE.
css classes:
.vissible{ display: block; } .invissible{ display: none; }
Js functions:
function setVisibility(val) {
var not = new Array;
not["none"] = "block";
not["block"] = "none";
for (i = 9; i <= 15; i++){
document.getElementById("cand" + i).style.display = val;
}
document.getElementById("hide_cand").style.display = val;
document.getElementById("view_cand").style.display = not[val];
}
function setVisibilityByClass(val) {
var not = new Array;
not["invissible"] = "vissible";
not["vissible"] = "invissible";
for (i = 9; i <= 15; i++){
document.getElementById("cand" + i).setAttribute("class", val);
}
document.getElementById("hide_cand").setAttribute("class", val);
document.getElementById("view_cand").setAttribute("class", not[val]);
}
I hope this helps:
(function() {
"use strict";
var candidates = {
idx: 0,
getElement: function(id) { return document.getElementById(id); },
toggle: function(elmnts, obj) {
var idx = candidates.idx,
getElement = function(id) { return candidates.getElement(id); };
if (elmnts.length) {
while ( idx < elmnts.length ) {
getElement(elmnts[idx]).style.display = obj.display;
idx++;
}
}
}
};
var idsToHide = [
"cand9", "cand10", "cand11", "cand12",
"cand13", "cand14", "cand15", "hide_cand"
];
var idsToShow = [
"cand9", "cand10", "cand11", "cand12",
"cand13", "cand14", "cand15", "hide_cand"
];
function showCandidates() {
candidates.toggle(idsToShow, {
display: "block"
});
candidates.toggle(["view_cand"], { display: "none" });
}
function hideCandidates() {
candidates.toggle(idsToHide, {
display: "none"
});
candidates.toggle(["view_cand"], { display: "block" });
}
})();
Easy to do with jQuery:
$(document).ready(function(){
$("#candidates").toggle(function (){
$(this).text('Hide Candidates');
$.each($('.candidate'), function() {
$(this).show();
});
}, function() {
$(this).text('Show Candidates');
$.each($('.candidate'), function() {
$(this).hide();
});
});
});
HTML:
Show Candidates
<div class='candidate' id='1'>
<h1>Hello</h1>
</div>
<div class='candidate' id='2'>
<h1>Hello</h1>
</div>
<div class='candidate' id='3'>
<h1>Hello</h1>
</div>
CSS:
.candidate { display: none }
Here's a JS fiddle: http://jsfiddle.net/vbh5T/
If you don't want to use jQuery then please ignore my answer.
(1) First of all, doing these kinds of lookups is best done with jquery. Apart from being easier (see code below), it also allows you pre-calculate the set of elements to act on. This matters, because lookups by ID scan the whole document tree. Accordingly, the more elements in the page, the slower it is to recalculate the set of elements to act on.
(2) Rather than setting individual properties, it is much better to use a css class.
<style>
.invisible {display:none !important;}
</style>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script type="text/javascript" charset="utf-8"> // <![CDATA[
$(document).ready(function(){
var hide = function(i) {i.addClass('invisible');};
var show = function(i) {i.removeClass('invisible');};
var candidates = $("#cand9, #cand10 /* etc. [...] */");
/* or, if you rejig this to set a class on all candidate elements:
var candidates = $(".candidate"); */
var hide_cand = $("#hide_cand");
var view_cand = $("#view_cand");
function showCandidates()
{
show(candidates);
show(view_cand);
hide(hide_cand);
}
});
// ]]>
</script>
I leave the corresponding hideCandidates as an exercise for the reader.

Resources