om/react: manipulate elements outside the render target element - javascript

I'm using om as a clojurescript react interface.
One question, which I guess relates to both om and react:
Inside my html body I have a div of the id "app", which is used for om/react as a render target.
What would be a prefered way to change attributes outside of this element. more concretely I need to set some stylesheets to the body.
Now, more clojure specific:
How do you set multiple key-value pairs to a javascript object. (e.g. document.body.style)
I'm using this:
(doseq [[k v] {"backgroundColor" "red" "overflow" "hidden" ...}]
(aset js/document.body.style k v))
There was a nice way to do so with underscore.js:
_.extend(document.body.style, {"backgroundColor": "red" "overflow": "hidden"})
Well, but this was the question here. Maybe it's not really needed because there is a special om/react way to go.

A nicer way to do this is simply set the body style with a javascript object containing all key-value pairs:
(set! (.. js/document -body -style) #js {:backgroundColor "red" :overflow "hidden"})

The solution provided by Naomi is great, but it uses the bad practice of inline css. Instead of setting the actual css styles in code, I would set a class to the desired html object, and in the styles sheets define the css properties of that class.
For example:
(set! (.. js/document -body -className) "my-class")

Related

Style classList vs className in JavaScript

I have two DOM elements, A and B, and want to get all style classes from A, and append them to B.
I understand that I can append new classes to element, without wiping existing ones, this way:
B.classList.add("cls1", "cls2", "cls3");
or this way:
var clsa = ["cls1", "cls2", "cls3"];
B.classList.add(...clsa);
Now, my question is, how can I get a list of classes in A, in the form of clsa array?
I can do this by using A.className property, then splitting it into an array.
Or I can simply append A.className to the B.className, as easily as:
B.className += A.className;
Or I can iterate components of A.classList object, 0: ... N:.
The second one looks most natural. However, there's this feeling that the classList, not the className, is the preferred "home" for style classes of an object. Is that right?
And if so, is there any elegant and clear way to do so, without resorting to using className?
Maybe something like:
B.classList.add(...A.classList.all);
Unobvious, but appears to work in Chrome:
B.classList.add(...A.classList);
You can first try to cast classList to an array like it is answered in Why doesn't .includes() work with .classList? , and then append using spread operator.
B.classList.add([...A.classList]);

Changing inner text value of tab through javascript

I'm learning Javascript right now, and attempting to change the text title of a particular tab. It's actually part of a larger Shiny dashboard project, but I want to add some custom functionality to a few tabs. Below are the tabs in question:
Simple enough. I first access my tabs in my Javascript file:
var tabScrub2 = $(document).find('[data-value="scrubTab2"]');
console.log(tabScrub2);
When I use Firefox's developer console, I see that the tab is an object:
Moreover, it looks like I need to change the innerText property of 0, whatever this is, since that corresponds to the title of my tab (the innerText of 1 corresponds to the text inside scrubTab2). However, I'm not familiar with the actual object type being returned here:
Simply put, how the heck do I access and manipulate properties from this? And am I actually accessing an array? When I type in
var scrub2 = tabScrub2["1"];
console.log(scrub2);
I get an HTML element. I'm seen the a element in CSS and jQuery, but am not super familiar with how to manipulate its properties programmatically? How do I go about accessing and manipulating the innerText properties of this via Javascript? For instance, how would I hide scrubTab2, or change its title to something else?
The first object you're seeing is jQuery's wrapper around the real DOM elements. It's not an actual array, but it does contain all of the elements that matched your query under zero-indexed properties (e.g. "0" and "1") which allows you to access to them via an array-like API (e.g. tabScrub[1]).
Your method of grabbing a node using tabScrub2["1"] is correct (see this question in the jQuery FAQ). It's more likely to see that done with a numeric key though (i.e. tabScrub[1]) because that matches the way you would access an element in a normal array.
As far as manipulating properties of the DOM node, the DOM's API is notoriously inconsistent and quirky (hence the need for things like jQuery in the first place). However, for your use case you can just assign a string to the innerText property directly (e.g. tagScrub2[1].innerText = "Tab title"). MDN is a great resource if you're looking for reference material on other parts of the DOM.
A side note: if you're looking for a specific element you should use a query that will only match that element. It's generally a bad sign if you're grabbing extra elements and then accessing the element you want at a key other than 0. If you're doing this then your code depends on other (potentially unrelated) nodes in the DOM existing before your node, and if/when you change those nodes your original code will break.
Just use jQuery eq method to get the relevant object index from the array.
For an example
//Query and get first element.
var tabScrub2 = $(document).find('[data-value="scrubTab2"]:eq(0)');
//Hide
tabScrub2.hide();
//Change title
tabScrub2.attr("title", "New Title Text");
Lean more about jQuery eq here.
https://api.jquery.com/eq/
Since you use jquery selectors tabScrub2[0] returns the native DOM element instead of another jQuery object. Therefore the hide function won't work in that object since the native DOM element doesn't implement such type of functionality for an element. That's why you have to use jQuery pseudo selector as above. Because hide will only work with a jQuery object.

apply custom style string to div using jquery

I have a javascript variable with the following value:
var style ="border-left:12px solid;border-color:red;position:relative;"
How can I apply it to a div like below:
<div class='my-div'></div>
I tried:
$('.my-div').css({style});
but failed spectacularly :(
var style = {
borderLeft:'12px solid',
borderColor:'red',
position:'relative'
}
The object should look like this. The CSS method takes in an object param. Doing a style string concat will work, but it's ugly.
Or use a class and be simple.
$('.my-div').css(style); //append style object to jQuery object
Note that using this object notation can also be beneficial because the properties can have logic in them, say for animations, or changing value dependencies.

Assign all vendor-prefixed variants to style

Modernizr provides a testAllProps() method which conveniently tests all the vendor prefixed styles of the one given to see if the style is supported by the currently running browser.
However I have no come to a point where I need to actually assign these properties from javascript because of various reasons that boil down to it being too cumbersome to conditionally link CSS files.
So for instance I could build an array and a routine which assigns each vendor specific style to the style of my target element:
['mozTransitionDuration', 'webkitTransitionDuration', 'oTransitionDuration', 'msTransitionDuration', 'transitionDuration'].map(function(s){ element.style.s = "style_setting"; });
Well, this will probably generate a bunch of errors because I will try to assign "style_setting" to 4 or 5 undefined values.
Does anybody know anything to make this a bit less painful?
Probably best to use an existing library that knows all about this stuff:
Prefix Free will let you assign styles from CSS without vendor-prefixing. There is also a jQuery Plugin for it that will allow you to do the same from JavaScript.
Before setting the value, check whether the property is undefined:
['mozTransitionDuration', 'webkitTransitionDuration', 'oTransitionDuration', 'msTransitionDuration', 'transitionDuration']
.map(function(s) {
if (element.style[s] != undefined) element.style[s] = "style_setting";
});

Javascript attributes compact

Is there a way to assign attributes in a more compact manner
I dont really want to use setAttribute as it seems to be buggy in ie8
This list is for all attributes so its quite long
else if(a=="textalign")
{
e.style.textAlign="";
e.align=v
}
if(a=="textalign")
{
e.style.textAlign="";
e.align=v
}
I don't know why you are trying to set alignment via an HTML attribute rather than just using the CSS... this is much less reliable as there are many elements which have no align attribute. HTML align is also deprecated and should be avoided in general.
You don't say what the “other attributes” are that you might want to set. If you are talking specifically about HTML attribute properties it's easy to set them by a name in a string:
e[a]= v;
But then you need a to be the HTML attribute property name, which would be ‘align’ not ‘textalign’. It wouldn't do anything special to try to workaround CSS overrides like textAlign, because there is no automated way to do that, and the interaction between the deprecated HTML styling attributes and CSS is ill-defined. Stick to attributes or CSS (CSS is highly preferable); don't use both.
If you are talking about setting any CSS style property, as I might guess from the name being ‘textalign’, that's done similarly:
e.style[a]= v;
But then, again, you'd want to be using the exact style property name ‘textAlign’ not ‘textalign’.
If you want to set CSS style properties by their CSS name, like ‘text-align’, you could transform that to the DOM name automatically:
// convert foo-bar-baz to fooBarBaz
//
var doma= a.replace(/-([a-z])/g, function(m, g) {
return g.toUpperCase();
});
e.style[a]= v;
If you really do need to use case-lossy names like ‘textalign’ you'd have to use a lookup of all property names you wanted to use to get the case back:
var propernames= ['textAlign', 'borderColor', 'paddingTop']; // etc
for (var i= propernames.length; i-->0;)
if (propernames[i].toLowerCase()===a)
a= propernames[i];
e.style[a]= v;
Forget setAttribute. It has nothing to do with style properties (it's a bug in IE6-7 that it even works on styles there), and you shouldn't use it on elements either for HTML documents, as there are other IE6-7 bugs to contend with there. Stick to the ‘DOM Level 2 HTML’ direct property access stuff, which is more reliable and easier to read.
Use a class instead of giving all the attribute values.
.testClass
{
// set all attribute values here
}
e.className = "test";
See
element.className
Use some framework such as JQuery, it takes care of all of your browser incompatibility issues. In JQuery you use the .css('attributeName', 'value')method.
jQuery would make that easy with .attr({attr1: val, attr2: val}) etc. It would also shield you from many cross-browser compatibility bugs.

Categories

Resources