Styling Google Translate widget for mobile websites - css
My website - www.forex-central.net - has the Google Translate drop-down widget on the top right of every page.
Only problem is it's a bit too wide for my website (5 cm), I would need a 4 cm version (which I've seen on other sites so I know this is possible)...but I have no idea how to tweak the code.
The code Google supplies for the widget I use is:
<script type="text/javascript">function googleTranslateElementInit() { new google.translate.TranslateElement({ pageLanguage: 'en', gaTrack: true, layout: google.translate.TranslateElement.InlineLayout.SIMPLE }, 'google_translate_element');}</script><script type="text/javascript" src="//translate.google.com/translate_a/element.js?cb=googleTranslateElementInit"></script>
Any help would be greatly appreciated! I'm a bit of a novice and have searched for hours on this, not getting anywhere :-/
Something like this will get you started:
.goog-te-menu-frame {
max-width:100% !important; //or whatever width you want
}
However, you would also need to do something like:
.goog-te-menu2 { //the element that contains the table of options
max-width: 100% !important;
overflow: scroll !important;
box-sizing:border-box !important; //fixes a padding issue
height:auto !important; //gets rid of vertical scroll caused by box-sizing
}
But that second part can't actually be done because the translate interface is included in your page as an iframe. Fortunately, it doesn't have its own domain, so we can access it via Javascript like this:
$('.goog-te-menu-frame').contents().find('.goog-te-menu2').css(
{
'max-width':'100%',
'overflow':'scroll',
'box-sizing':'border-box',
'height':'auto'
}
)
But that won't work until the element actually exists (it's being loaded asynchronously) so we have to wrap that in something that I got here. Put it all together, you get this:
function changeGoogleStyles() {
if($('.goog-te-menu-frame').contents().find('.goog-te-menu2').length) {
$('.goog-te-menu-frame').contents().find('.goog-te-menu2').css(
{
'max-width':'100%',
'overflow':'scroll',
'box-sizing':'border-box',
'height':'auto'
}
)
} else {
setTimeout(changeGoogleStyles, 50);
}
}
changeGoogleStyles();
Whew.
You can use that same strategy to apply other styles to the translate box or perhaps alter the table styles to have it flow vertically instead of scroll horizontally offscreen, whatever. See this answer.
EDIT:
Even this doesn't work, because Google re-applies the styles every time you click the dropdown. In this case, we try and change height and box-sizing, but Google reapplies over those, while overflow and max-width stick. What we need is to put our styles somewhere they won't get overriden and add !importants [cringes]. Inline styles will do the trick (I also replaced our selector with a variable for succinctness and what is likely a negligible performance boost):
function changeGoogleStyles() {
if(($goog = $('.goog-te-menu-frame').contents().find('body')).length) {
var stylesHtml = '<style>'+
'.goog-te-menu2 {'+
'max-width:100% !important;'+
'overflow:scroll !important;'+
'box-sizing:border-box !important;'+
'height:auto !important;'+
'}'+
'</style>';
$goog.prepend(stylesHtml);
} else {
setTimeout(changeGoogleStyles, 50);
}
}
changeGoogleStyles();
The Google Translate widget creates an iframe with content from another domain (several files from Google servers). We would have to manipulate the content inside the iframe, but this so-called cross-site scripting did not work for me. I found another solution. I downloaded two of the many files which the widget uses, so I could edit them.
Bear in mind that Google can change its API anytime. The hack will have to be adapted then.
Prerequisite:
I assume that the widget is working on your website. You just want to fit it on smaller screens. My initial code looks like:
<div id="google_translate_element"></div>
<script type="text/javascript">
function googleTranslateElementInit()
{
new google.translate.TranslateElement({pageLanguage:'de', layout: google.translate.TranslateElement.InlineLayout.SIMPLE}, 'google_translate_element');
}
</script>
<script type="text/javascript" src="//translate.google.com/translate_a/element.js?cb=googleTranslateElementInit"></script>
If your initial code looks different, you might have to adapt your solution accordingly.
Special tools used:
Chrome DevTools (adapt for other browsers)
Procedure:
In Google Chrome, right-click on your page containing the Google Translate widget.
Click Inspect. A window or side pane will apper with lots of HTML info.
In the top line, select the Sources tab.
Browse the sources tree to
/top/translate.google.com/translate_a/element.js?cb=googleTranslateElementInit
Click the file in the tree. The file content will be shown.
Under the code window of element.js, there is a little button with two curly brackets { }. Click this. It will sort the code for better readability. We will need this readability in the next steps.
Right-click inside the element.js code > Save as…. Save the file inside the files hierarchy of your website, in my case:
/framework/google-translate-widget/element.js
Point your <script> tag to the local element.js.
<!--<script type="text/javascript" src="//translate.google.com/translate_a/element.js?cb=googleTranslateElementInit"></script>-->
<script type="text/javascript" src="../framework/google-translate-widget/element.js?cb=googleTranslateElementInit"></script>
From now on, your website should load element.js from its local directory. Now is a good moment to check if your Google Translate widget still works. Also check in Chrome DevTools where the browser has taken the file from (Google server or local directory). It should sit in the sources tree under
/top/[your domain or IP]/framework/google-translate-widget/element.js?cb=googleTranslateElementInit
We need another file from Google servers. Browse the sources tree to
/top/translate.googleapis.com/translate_static/css/translateelement.css
Download this file after clicking the curly brackets { }. I saved it in my website files directory as
/framework/google-translate-widget/translateelement.css
In your website files directory, open element.js and change line 66:
//c._ps = b + '/translate_static/css/translateelement.css';
c._ps = '/framework/google-translate-widget/translateelement.css';
From now on, your website will also load translateelement.css from its local directory. Check this now.
Open your local translateeleent.css and append the following styles at the end:
/* Make all languages visible on small screens. */
.goog-te-menu2 {
width: 300px!important;
height: 300px!important;
overflow: auto!important;
}
.goog-te-menu2 table,
.goog-te-menu2 table tbody,
.goog-te-menu2 table tbody tr {
width: 100%!important;
height: 100%!important;
}
.goog-te-menu2 table tbody tr td {
width: 100%!important;
display: block!important;
}
.goog-te-menu2 table tbody tr td .goog-te-menu2-colpad {
visibility: none!important;
}
I borrowed the code from another answer: Google translate widget mobile overflow
The geometry might work now, but we broke another thing. The widget text showing “Select Language”, “Sélectionner une langue”, or whatever it says in you language, is locked to that language now. Since you want your other-language readers to understand the offer, the widget should adapt to their language as it used to work before our hack. Also, the listed languages’ names are affected. The reason for this bug can be found in the file element.js, which was silently tailored to our browser’s language setting. Look in element.js on lines 51 and 69
c._cl = 'fr';
_loadJs(b + '/translate_static/js/element/main_fr.js');
In my case, it was set to French (fr).
Correcting line 51 is as simple as
c._cl = 'auto'; //'fr';
Line 61 is trickier, because there is no 'auto' value available. There is a file main.js (without the _fr ending) available on Google servers, which provides English as a fallback, but we prefer the user’s language. Have a look in the file
/top/translate.googleapis.com/translate_a/l?client=…
It contains two objects. sl and tl meaning the source languages and target languages supported for translation. We have to check if the user’s browser is set to one of the target languages. There is a JavaScript constant navigator.language for this.
Edit element.js at line 69:
// determine browser language to display Google Translate widget in that language
var nl = navigator.language;
var tl = ["af","sq","am","ar","hy","az","eu","bn","my","bs","bg","ceb","ny",
"zh-TW","zh-CN","da","de","en","eo","et","tl","fi","fr","fy","gl",
"ka","el","gu","ht","ha","haw","iw","hi","hmn","ig","id","ga","is",
"it","ja","jw","yi","kn","kk","ca","km","rw","ky","ko","co","hr",
"ku","lo","la","lv","lt","lb","mg","ml","ms","mt","mi","mr","mk",
"mn","ne","nl","no","or","ps","fa","pl","pt","pa","ro","ru","sm",
"gd","sv","sr","st","sn","sd","si","sk","sl","so","es","sw","su",
"tg","ta","tt","te","th","cs","tr","tk","ug","uk","hu","ur","uz",
"vi","cy","be","xh","yo","zu"];
var gl = "";
if( tl.includes( nl )) gl = '_'+nl;
else
{
nl = nl.substring(0, 3);
if( tl.includes( nl)) gl = '_'+nl;
else
{
nl = nl.substring(0, 2);
if( tl.includes( nl)) gl = '_'+nl;
else gl = '';
}
}
_loadJs(b + '/translate_static/js/element/main'+gl+'.js');
//_loadJs(b + '/translate_static/js/element/main_fr.js');
… should do the trick.
Try using this in your CSS
.pac-container, .pac-item { width: 100px !important;}
where you can alter the with of the dropdown by altering 'the 100px' value.
This should work. Let me know if it doesn't and I'll have another look.
Related
Dynamically switch global CSS for Angular8 app (client branding)
I want to dynamically switch Angulars global CSS files based on which client is connecting. This will be used for client-branding purposes, including fonts, colors, photos, headers, footers, button-styles, etc. Each client has provided us with a CSS file, which we need to integrate into our app. We have hundreds of clients. Current solution is to try and override the CSS of individual components at load. This is bad because it adds a lot of boilerplate: Html: <link id="theme" rel="stylesheet" href="./assets/stylesheets/{{cclientCode}}.css"> ts: ngOnInit() { this.service.clientCode.subscribe(clientCode => this.clientCode = clientCode); } My workaround isn't working because the link html is called before the {{}} has a chance to load in the value. I'm also not motivated to fix my workaround because its just that -a workaround. Instead, I want to implement something that works globally, without any per-component boilerplate. What I want is the ability to dynamically switch the global Angular style for each client. So something like: "styles": [ "src/assets/stylesheets/angular_style.css", "src/assets/stylesheets/client_style.css" ] Where client_style.css is served differently to each client.
I've found a solution that I think is workable. It definitely has issues though, so if anyone has their own answer, please still share! First, I added a clientCode String field to SessionDataService, a global service I use to move component-agnostic data around my app: export class SessionDataService { clientCode: BehaviorSubject<String>; constructor(){ this.clientCode = new BehaviorSubject('client_default'); } setClientCode(value: String) { this.clientCode.next(value); } } Then, inside app.component.ts, I added a BehaviorSubject listener to bring in the value of clientCode dynamically: public clientCode: String; constructor(private service : SessionDataService) { this.service.clientCode.subscribe(clientCode => this.clientCode = clientCode); } Next, I added a wrapper around my entire app.component.html: <div [ngClass]="clientCode"> --> ALL app components go here (including <router-outlet>) </div> So at this point, I've created a system that dynamically adds client-code CSS classes to my components, including all children :) Finally, I just have to write CSS rules: .ClientOne p { color: red; } .ClientOne .btn { background-color: red; } .ClientTwo.dashboard { height: 15%; } I hope this helps somebody! Essentially the "trick" here is to add a ngClass that wraps the entire app, and then justify all client-specific CSS rules with their client code.
Scrollposition as a variable in css?
I'm working on a webpage with very complex non default scroll behaviour. Stuff is suppose to move over the screen based on how much you scroll and stuff like that. I know I can use javascript to add classes on different positions, something like this: wrap.on("scroll", function(e) { if (this.scrollTop > 147) { wrap.addClass("fix-search"); } else { wrap.removeClass("fix-search"); } }); But now I need the actual scrollposition as a value in css. I would like to do something like this, this should cause a object to fly up over the screen twice as fast as you are scrolling. .floatingObject { position: absolute; bottom: calc(var(--scrollPosition)*2); } Is something like this possible? Can you maybe store the value in the DOM and retrieve it as a css-variable/property etc?
Yes, In your scrollListener just add something like the following: document.documentElement.style.setProperty('--scrollPosition', this.scrollTop); In your css :root {--scrollPosition:0} Mind that this has no support in IE so you'll need a fallback everywhere you want to use the variable.
Anyone have solutions for table>th with position:sticky not working on FF & IE?
I have an idea to make sticky header of table and I have tried with position:sticky. It's working fine on Chrome but on Firefox and IE not working as I think. Below is my CSS .myTable--mof thead th { position: -webkit-sticky; position: sticky; top: 0; z-index:100; }
position:sticky is not supported for child table elements in some browsers. The 'why' I don't know.. Will it be supported in the future? I sure hope so! I recently wrote this jQuery solution. It'll work for simple tables with simple headers. Does not look for colspans or multiple rows in thead! I tried some plugins before, but they all listened for the scroll event which throws alerts in some browsers. They caused flickering/jumping in some cases, and a delay was noticable when hitting the position to stick at. Using position:sticky for other elements and liking those transitions more, I came up with the following piece of code. jQuery.fn.stickTableHeaders = function() { return this.each(function() { var table = $(this), header = table.find('thead'), sticked = $('<table></table>').addClass('table').append(header.clone()); // Needs to be wrapped in new table since table child elements can't be sticky? (FF) sticked.find('th').css({ // You'll have to copy the original thead (th's) CSS manualy 'backgroundColor': '#DEE5EA', 'color': '#606060', 'padding':'8px', 'color':'#606060' }).removeAttr('width'); // And remove the width attr from the clone th's since we'll be setting them again later sticked.find('th:not(:last-child)').css({ // More CSS 'borderRight': '1px solid #ddd' }); sticked.find('a').css({ // More CSS 'color':'#606060' }); // I tried different things, most of the original th's should have a width attribute set (not in CSS and avoid percent) for best results $(window).resize(function() { sticked.width(table.width()); sticked.find('th').each(function() { var headerTH = header.find('th').eq($(this).index()); if(headerTH.is('[width]') || headerTH.is(':first-child') || headerTH.is(':last-child')) { // First and last th are allready calculated by another function in my app. See what suits for you here... $(this).width(header.find('th').eq($(this).index()).width()); } else { var cellWidth = header.find('th').eq($(this).index()).width(true), tableWidth = table.width(true), percent = 100*(cellWidth/tableWidth); $(this).css({'width':percent+'%'}); } }); // We keep the original thead to avoid table collapsing, we just slide the whole table up. table.css({ 'marginTop':-header.height() }); }).trigger('resize'); // Apply stickyness sticked.css({ 'display':'table', 'position':'sticky', 'top':$('#header-menu').height(), // My sticky nav is my top position, adjust this to your needs 'zIndex':'10' }); // Insert clone before original table $(this).before(sticked); }); }; Now I just use this on each page load: $("table").stickTableHeaders(); You might want to filter out nested tables from the above selector... Hope this helps someone.
How can I prevent CSS from affecting certain element?
I am writing a GreaseMonkey script that sometimes creates a modal dialog – something like <div id="dialog"> Foo </div> . But what can I do if the site has something like #dialog { display: none !important; } ? Or maybe the owner of some site is paranoid and has something like div { display: none !important; } div.trusted { display: block !important; } because he doesn't want people like me adding untrusted content to his page. How can I prevent those styles from hiding my dialog? My script runs on all pages, so I can't adapt my code to each case. Is there a way to sandbox my dialog?
Actually a very interessting problem, here is another approach: adding an iframe and modifying it creates a seperate css space for you (your sandbox) look at this jsfiddle example: http://jsfiddle.net/ZpC3R/2/ var ele = document.createElement("iframe"); ele.id = "dialog"; ele.src = 'javascript:false;'; ele.style.height = "100px"; ele.style.width = "300px"; ele.style.setProperty("display", "block", "important"); document.getElementById("dialog").onload = function() { var d = document.getElementById("dialog").contentWindow.document; // ... do your stuff within the iframe }; this seems to work without problem in firefox. now you only have to make sure that the iframe is untouched, you can do this they way i described in my 1. answer
just create the div like this: var ele = document.createElement("div"); ele.style.setProperty("display", "block", "important"); that should overwrite all other styles afaik. look here, it seems to work: http://jsfiddle.net/ZpC3R/
How do URL fragment (the #stuff) interacts with CSS?
How does URL fragments interact with CSS? I have a page, say: http://example.boom/is_this_a_bug.html. The code for the page is shown in https://gist.github.com/3777018 When I load the page with the URL like that, the .tab-pane elements are not showed because they overflow their container, and it has an overflow: hidden property. However, if I load the page by appending a valid fragment (#00) to the URL, then the .tab-pane gets visible, just as if the left:100% was not taken into account. Pressing the button just removes and resets left:100%, and then I get the overflowing tab-panes. This happens in both Firefox 15.0.1 and Chromium 18.0.1025.168 (Developer Build 134367 Linux) Ubuntu 12.04. Any ideas why this is happening? Is this a bug, or is documented elsewhere? Best regards, Manuel.
When you load a page with a fragment identifier in the URL, if that fragment identifier matches the ID of an element on the page the browser will scroll the page to bring that element into view.
An alternative can be use javascript applied styles. (function hashStyle() { if (window.location.hash == '#COLOR') { var css = document.createElement('style'), s = document.getElementsByTagName('script')[0], styles = 'body { background-color: #b0c4de; }'; css.type = 'text/css'; if (css.styleSheet) { css.styleSheet.cssText = styles; } else { css.appendChild(document.createTextNode(styles)); } s.parentNode.insertBefore(css, s); } })();