(Please ignore the empty squares.)
without CSS view { height: 45em; }, I get: (position overlap)
with CSS view { height: 45em; }, I get: (unwanted, position mismatch)
How can I have the blue <span> element positioned correctly in the second case?
<view style="height: 45em;">
<pdf-page> <!-- position: relative -->
<text class="textLayer"> <!-- position: absolute -->
<span style="left: 417.34px; top: 37.8391px; ..."></span> <!-- position: absolute -->
</text>
<svg width="595px" height="842px" preserveAspectRatio="none" viewBox="0 0 595 842" xmlns="http://www.w3.org/2000/svg" version="1.1">
<g ⋯><g ⋯><text><tspan></tspan></text></g></g>
</svg>
</pdf-page>
</view>
Here is the complete case in stackoverflow (see /* ← */ in the second pane after clicking on Show code snippet):
#namespace url(http://www.w3.org/1999/xhtml);
#namespace svg url(http://www.w3.org/2000/svg);
/*pdf.css*/
:root {
--pdf-page-outline-color: #aaa;
--pdf-page-background-color: #fcfcfc;
}
pdf-file { display: contents; }
pdf-page {
display: inline-block;
outline: 1px solid var(--pdf-page-outline-color);
background-color: var(--pdf-page-background-color);
}
pdf-page { position: relative; }
/* text.css */
.textLayer {
position: absolute;
left: 0; top: 0; right: 0; bottom: 0;
width: 100%; height: 100%;
-overflow: hidden;
opacity: 1;
-line-height: 1;
}
.textLayer > span {
color: transparent;
position: absolute;
white-space: pre;
cursor: text;
-webkit-transform-origin: 0% 0%;
transform-origin: 0% 0%;
}
/**/
view { background: green; }
.textLayer { background: rgba(0, 255, 0, .1); }
svg|svg { background: rgba(255, 0, 0, .1); }
<style>
view {
height: 45em; /* ← */
display: flex;
overflow: auto;
flex-direction: column;
place-items: center;
scroll-snap-type: y mandatory;
overflow: auto;
}
pdf-page { height: 100%; scroll-snap-align: start; }
svg { height: 100%; width: auto; }
text { overflow: visible; background: rgb(0, 0, 0, .1); }
text > span { background: rgba(0,0,255,.1); }
</style>
<view -onclick="this.requestFullscreen()">
<pdf-page of="f" no="+1" svg="">
<text class="textLayer">
<span style="left: 417.34px; top: 37.8391px; font-size: 12px; font-family: sans-serif; transform: scaleX(1.07482);">Plenarprotokoll 16/3</span>
</text>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="595px" height="842px" preserveAspectRatio="none" viewBox="0 0 595 842">
<g transform="matrix(1 0 0 -1 -8 850)">
<g transform="">
<text transform="matrix(12 0 0 12 425.34 801.2976) scale(1, -1)" xml:space="preserve">
<tspan x="0 0.6672 0.9454 1.5016 2.1128 2.669 3.0582 3.6694 4.0586 4.6698 5.003 5.6142 6.1704 6.7816 7.0598 7.6132 8.1694 8.7256 9.0038" y="0" font-family="g_d0_f1" font-size="1px" fill="rgb(0,0,0)"></tspan>
</text>
</g>
</g>
</svg>
</pdf-page>
</view>
(also available for review on codepen: https://codepen.io/cetinsert/pen/MWeVxLe?editors=1100)
A much more precise way is to just transform: scale(x, y) the <text> layer once on resize without any <span style> position value recalculations / unit change.
This answer has triggered the launch of my commercial project.
– https://WebPDF.pro
Zero-dependency, truly HTML-native PDF web components.
const t = document.querySelector('text');
const r = new ResizeObserver(textResize(t));
r.observe(t);
const textResize = t => ([ a ]) => {
const e = t.parentNode.lastElementChild; // <svg> | <canvas>
const i = PDFPageElement.image(e); // { height, width };
const h = e.clientHeight;
const x = h / i. height;
const w = e.clientWidth;
const y = w / i. width;
t.style.setProperty('transform', `scale(${x}, ${y})`);
};
PDFPageElement.image = i => { if (!i) return;
switch (i.tagName) {
case 'CANVAS': return { height: i.height, width: i.width };
default: /*SVG*/ return { height: i.height.baseVal.value, width: i.width.baseVal.value };
}
};
with 1 additional CSS rule
.textLayer { overflow: visible; }
Before / After
Given viewport width and height, a one-time conversion from <span style> pixels to percents:
const px2pc = ({ width, height }) => s => {
const c = s.style;
const l = +c.getPropertyValue('left' ).slice(0, -2); // drop px
const t = +c.getPropertyValue('top' ).slice(0, -2);
const f = +c.getPropertyValue('font-size').slice(0, -2);
c.setProperty ('left', `${(l / width) * 100}%`);
c.setProperty ('top', `${(t / height) * 100}%`);
c.setProperty ('font-size', `${(f / height) * 100}%`);
};
and accounting for font size adaptation in <text> element whenever its ancestors cause resize:
const t = document.querySelector('text');
const r = new ResizeObserver(textFontResize(t));
r.observe(t);
const textFontResize = t => ([ a ]) => {
const i = t.parentNode.lastElementChild; // <svg> | <canvas>
t.style.setProperty('font-size', `${i.clientHeight}px`);
};
proved itself a very robust and relatively simple solution.
(If anyone comes up with a more elegant way, say without ever resorting to ResizeObserver, please post a new answer.)
Demo
(External assets are version-fixed for this question.)
Scroll to the end of this answer
Hit ▶️ Run code snippet
Hit ⤤ Full page
<!doctype html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="//cdnjs.cloudflare.com/ajax/libs/pdf.js/2.6.347/pdf.min.js" integrity="sha512-Z8CqofpIcnJN80feS2uccz+pXWgZzeKxDsDNMD/dJ6997/LSRY+W4NmEt9acwR+Gt9OHN0kkI1CTianCwoqcjQ==" crossorigin="anonymous"></script>
<script src="//shin.glitch.me/shin.q1.js"></script>
<script src="//shin.glitch.me/pdf.q1.js"></script>
<!-- private resources -->
<link href="//cdn.blue/{fa-5.15}/css/all.css" rel="stylesheet">
<link href="//cdn.blue/{fa+}/var.css" rel="stylesheet">
<link href="//cdn.blue/{fa+}/x.css" rel="stylesheet">
<!-- private resources -->
<style>:root { max-width: 50em; margin: auto; }</style>
<script>console.clear();</script>
<style>html, body { padding: 0; margin: 0; font-family: system-ui; }</style>
<script>
class CodeEditElement extends ShinElement {
constructor() {
super(`
<style>:host { display: block; overflow: hidden; } pre { height: 100%; margin: 0; }</style>
<pre contenteditable spellcheck=false inputmode=text></pre>`, { _: { QS: { T: [ 'pre' ] } } });
const e = this;
e.ph = v => { const e = v.target; if (!e.childElementCount) return; e.textContent = e.textContent; };
}
connectedCallback() { this._.pre. addEventListener('input', this.ph); }
disconnectedCallback() { this._.pre.removeEventListener('input', this.ph); }
get textContent() { return this._.pre.textContent; }
set textContent(v) { this._.pre.textContent = v; }
}
CodeEditElement.define();
class CodeLiveElement extends ShinElement {
constructor() { super(`<live></live>`, { _: { QS: { T: [ 'live' ] } } }); }
get textContent() { return this._.live.textContent; }
set textContent(v) { this._.live.textContent = v; }
get innerHTML() { return this._.live.innerHTML; }
set innerHTML(v) { this._.live.innerHTML = v; this.evalScripts(); }
evalScripts() { this._.QA('script').forEach(s => eval(s.textContent)); }
}
CodeLiveElement.define();
class CodePlayElement extends ShinElement {
constructor() {
super(`
<style>
:host(:not([open])) > code-edit { display: none; }
:host > div { display: flex; justify-content: stretch; align-items: stretch; }
::slotted(select) { flex: 1; }
* { border-color: var(--bd); }
</style>
<div part=controls>
<slot></slot>
<button id=reset><slot name=reset></slot></button>
<button id=open><slot name=open></slot></button>
</div>
<code-edit id=pre part=edit></code-edit>`,
{ _: { QS: { S: { '#pre': 'pre', '#reset': 'reset', '#open': 'open' } } } }
);
const e = this;
e.sc = v => { const tx = e.tx; e.px = tx; };
e.pi = v => { e.t.ux = e.px; };
e.rc = v => { e.tr(); };
e.oc = v => { e.open =!e.open; };
Shin.IPA(e, 'open', { t: Shin.Boolean });
}
connectedCallback() { setTimeout(() => this._init()); }
disconnectedCallback() { this._dest(); }
static cleanCode(t = "") {
return t.trim().replace(/^[\n]+/g, '').replace(/\s+$/g, '').split('\n').map(l => l.trimEnd()).join('\n');
}
get s() { return this.QS('select'); }
get S() { const o = this.QA('option'); return o.filter(o => o.selected); }
get t() { return [].concat(...this.S.map(o => Shin.QA('template', o))); }
get tx() { return this.t.map(t => t.ux || this.constructor.cleanCode(t.innerHTML)).join('\n'); }
tr() { this.t.ux = undefined; this.sc(); }
get r() { return this._.reset; }
get o() { return this._.open; }
get p() { return this._.pre; }
get P() { return this._.QA('pre'); }
get px() { return this.p.textContent; }
set px(v) { this.p.textContent = v; this.oninput(); }
_init() {
const e = this; e.sc();
e.s.addEventListener('change', e.sc);
e.p.addEventListener('input', e.pi);
e.r.addEventListener('click', e.rc);
e.o.addEventListener('click', e.oc);
}
_dest() {
const e = this;
e.s.removeEventListener('change', e.sc);
e.p.removeEventListener('input', e.pi);
e.p.removeEventListener('click', e.rc);
e.p.removeEventListener('click', e.oc);
}
}
CodePlayElement.define();
</script>
<style>
body { padding: 1em; overflow: scroll; font-family: system-ui; }
:root {
--list-bg: #eee;
--code-bg: #fefefe;
--live-bg_: #ccc;
--bd: #ccc;
}
code-play { display: flex; width: 100%; flex-direction: row-reverse; }
code-play:not(:first-of-type) { margin-top: 1em; }
::part(edit) { min-height: 1em; min-width: 1em; overflow-x: auto; background-color: var(--code-bg); }
x[undo]:before, x[undo]:after { content: var(--fa-undo); }
x[open]:before, x[open]:after { content: var(--fa-eye-slash); }
[open] x[open]:before,
[open] x[open]:after { content: var(--fa-eye); }
select { background: var(--list-bg); border-color: var(--bd); overflow: auto; }
live { background: var(--live-bg); display: block; bordxer: 1px solid var(--bd); }
code-play:not([open]) + live { _display: none; }
::part(edit) { border: 1px solid var(--bd); flex: 1; }
::part(controls) { flex-direction: column-reverse; }
::part() { border-radius: 3px; }
</style>
<style>
code-play:not([open]) { height: 2.7em; _outline: 1px solid; }
code-play:not([open]) > select { display: none; }
</style>
</head>
<body>
<code-live id="cl"></code-live><script>cl.innerHTML = "";</script>
<script>
const pes = 'previousElementSibling';
const n = (p, N = 1) => e => { let j = e[p]; for (let i = 1; i < N; i++) j = j[p]; return j; };
const c = n(pes, 2);
const l = n(pes, 1);
const _ = () => document.currentScript;
</script>
<code-play open>
<select multiple size="1">
<option selected>file<template>
<pdf-file id="f" src="//pdf.systems/16003.pdf"></pdf-file>
<pdf-file id="g" src="//pdf.systems/16004.pdf"></pdf-file>
</template></option>
</select>
<x open slot="open"></x>
<x undo slot="reset"></x>
</code-play>
<live></live>
<script>{
const _ = document.currentScript;
c(_).oninput = () =>
l(_).innerHTML = c(_).px;
}
</script>
<code-play open style="min-height: 11em;">
<select multiple size="6">
<optgroup label="File Reference">
<option>by attribute<!-- !!!!!!!!! --><template>
<pdf-page of="f" no="+1" scale=".1"></pdf-page>
<pdf-page of="f" no="+1" scale=".2"></pdf-page>
<pdf-page of="f" no="+1" scale=".3"></pdf-page>
<pdf-page of="f" no="+1" scale=".4"></pdf-page>
<pdf-page of="f" no="+1" scale=".5"></pdf-page>
<pdf-page of="f" no="+1" scale=".5" svg=""></pdf-page>
</template></option>
<option>by ancestry<!-- !!!!!!!!! --><template>
<pdf-file src="//pdf.systems/16008.pdf">
<pdf-page no="+1" scale=".4" svg></pdf-page>
<pdf-page no="+3" scale=".4" svg></pdf-page>
<pdf-page no="-1" scale=".4" svg></pdf-page>
</pdf-file>
</template></option>
</optgroup>
<optgroup label="Embed Mode">
<option selected>Sized Container ⭤<!-- !!!!!!!!! --><template>
<style>
view { width: 10em; height: 25em; /* ← */
display: block; background: white; overflow: auto;
}
pdf-page { width: 100%; }
::part(layer) { width: 100%; height: auto; }
</style>
<view onclick="this.requestFullscreen()">
<pdf-page of="f" no="+1" xvg="" scale=".2"></pdf-page>
<pdf-page of="f" no="+1" xvg="" scale="1"></pdf-page>
<pdf-page of="f" no="+1" xvg="" scale="2"></pdf-page>
<pdf-page of="f" no="+1" svg=""></pdf-page>
<pdf-page of="f" no="-1" xvg=""></pdf-page>
<pdf-page of="g" no="-1" svg=""></pdf-page>
</view>
</template></option>
</optgroup>
</select>
<x open slot="open"></x>
<x undo slot="reset"></x>
</code-play>
<live></live>
<script>{ const _ = document.currentScript; c(_).oninput = () => l(_).innerHTML = c(_).px; }</script>
<style>live { display: flex; align-items: flex-end; flex-wrap: wrap; } pdf-file { display: contents; }</style>
<h3>Styling</h3>
<p>Styles can be easily applied. (Try <strong><kbd>Ctrl</kbd></strong> + <i class="fa fa-mouse-pointer"></i> to unselect / select multiple.)</p>
<code-play open>
<select multiple size="8">
<optgroup label="Page">
<option>outline <template><style>pdf-page { outline: 1px dotted; }</style></template></option>
<option>background<template><style>pdf-page { background-color: rgb(200, 200, 255, .1); }</style></template></option>
</optgroup>
<optgroup label="Text">
<option selected>mark<template><style>::part(span) { background-color: rgb(255, 0, 0, .1); }</style></template></option>
</optgroup>
<optgroup label="Image">
<option>hidden <template><style>::part(image) { opacity: 0; }</style></template></option>
<option>pixelated <template><style>::part(image) { image-rendering: pixelated; }</style></template></option>
<option>crisp-edges<template><style>::part(image) { image-rendering: crisp-edges; }</style></template></option>
</optgroup>
</select>
<x open slot="open"></x>
<x undo slot="reset"></x>
</code-play>
<live></live>
<script>{ const _ = document.currentScript; c(_).oninput = () => l(_).innerHTML = c(_).px; }</script>
<script>
document.addEventListener( 'load', e => console.warn('l', e.target));
document.addEventListener('unload', e => console.warn('u', e.target));
</script>
<p style="margin-bottom: 10em;">Documentation (WIP)</p>
</body>
</html>
It's not really possible to do via CSS in a clean way. This, for example, will work, but because you're positioning a span on top of a picture, all of the numbers are hardcoded:
.textLayer > span{
right: 10% !important;
left: auto !important;
top: 0 !important;
margin-top: 6%;/*margin-top uses 6% of the WIDTH, not 6% of the height. It's very useful when trying to place something on top of an image.*/
width: 20%;
height: 2%;
}
Here's a reproduction of your snippet with the CSS added in:
#namespace url(http://www.w3.org/1999/xhtml);
#namespace svg url(http://www.w3.org/2000/svg);
/*pdf.css*/
:root {
--pdf-page-outline-color: #aaa;
--pdf-page-background-color: #fcfcfc;
}
pdf-file { display: contents; }
pdf-page {
display: inline-block;
outline: 1px solid var(--pdf-page-outline-color);
background-color: var(--pdf-page-background-color);
}
pdf-page { position: relative; }
/* text.css */
.textLayer {
position: absolute;
left: 0; top: 0; right: 0; bottom: 0;
width: 100%; height: 100%;
-overflow: hidden;
opacity: 1;
line-height: 1;
}
.textLayer > span {
color: transparent;
position: absolute;
white-space: pre;
cursor: text;
-webkit-transform-origin: 0% 0%;
transform-origin: 0% 0%;
}
.textLayer > span{
right: 10% !important;
left: auto !important;
top: 0 !important;
margin-top: 6%;/*margin-top uses 6% of the WIDTH, not the height. It's sometimes more useful than ordinary top:6%.*/
width: 20%;
height: 2%;
}
/**/
view { background: green; }
.textLayer { background: rgba(0, 255, 0, .1); }
svg|svg { background: rgba(255, 0, 0, .1); }
<style>
view {
height: 45em; /* ← */
display: flex;
overflow: auto;
flex-direction: column;
place-items: center;
scroll-snap-type: y mandatory;
overflow: auto;
}
pdf-page { height: 100%; scroll-snap-align: start; }
svg { height: 100%; width: auto; }
text { overflow: visible; background: rgb(0, 0, 0, .1); }
text > span { background: rgba(0,0,255,.1); }
</style>
<view -onclick="this.requestFullscreen()">
<pdf-page of="f" no="+1" svg="">
<text class="textLayer">
<span style="left: 417.34px; top: 37.8391px; font-size: 12px; font-family: sans-serif; transform: scaleX(1.07482);">Plenarprotokoll 16/3</span>
</text>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="595px" height="842px" preserveAspectRatio="none" viewBox="0 0 595 842">
<g transform="matrix(1 0 0 -1 -8 850)">
<g transform="">
<text transform="matrix(12 0 0 12 425.34 801.2976) scale(1, -1)" xml:space="preserve">
<tspan x="0 0.6672 0.9454 1.5016 2.1128 2.669 3.0582 3.6694 4.0586 4.6698 5.003 5.6142 6.1704 6.7816 7.0598 7.6132 8.1694 8.7256 9.0038" y="0" font-family="g_d0_f1" font-size="1px" fill="rgb(0,0,0)"></tspan>
</text>
</g>
</g>
</svg>
</pdf-page>
</view>
Edit: A bit more clarification.
We want to put the box on top of the text. The numbers used for the position and width/height of the text may seem arbitrary, but that's simply because the location of the item we're trying to cover ALSO has an arbitrary location/width/height. (If you like, we can talk about how to use GIMP to check the aspect ratio of your image, but..
a. I don't think using GIMP to measure the correct values is within the scope of this answer (You can math it out by taking the width of the image and height of the image to find the aspect ratio, and then use that aspect ratio along with the X/Y coordinates of the start point and the X/Y coordinates of the end point to figure out what percentages you need to use.... but, well....)
b. It's usually significantly faster to just fiddle with it in Chrome's Dev Tools for 15 minutes,
As a general rule, when using position: absolute to put something on top of an image, your code is going to look something like this:
.item{
position:absolute;
top:0;
margin-top:W%; //The reason we use margin instead of top is because margin is based off width, which allows us to maintain aspect ratio on our positioning.
left:X%; // Or right
width:Y%;
height:Z%;
}
Edit 2: I'd originally used vw and vh, which are often extremely useful for this kind of positioning, but in the end it was possible to refactor them out, which is why the only non-standard positioning we're using is margin-top.
I'm trying to remove an element's class on an event. I'm using jQuery library, and using the function removeClass to remove the element's class.
However, when inspecting the element's classes, the class I removed is not removed off the list.
In order to inspect the element's classes, there is the following code-block:
console.log($zoomToolOBJ.attr('class'));
$zoomToolOBJ.removeClass('onTool');
console.log($zoomToolOBJ.attr('class'));
As I view the console, I do see onTool class in both console.log (means the class onTool was not removed).
Here is my code:
<!DOCTYPE html>
<html>
<head>
<title>Ado designed</title>
<style>
.tools {
display: flex;
flex-direction: column;
overflow: hidden;
padding: 8px;
border-radius: 8px;
box-shadow: 3px 3px 3px #c5c5c5,
-3px 3px 3px #c5c5c5;
}
.toolIcon {
cursor: pointer;
transition: all .3s ease;
height: 60px;
width: 60px;
padding: 15px;
border-radius: 8px;
fill: #4d4d4d;
}
.toolIcon:not(:last-child) {
margin-bottom: 10px;
}
.toolIcon:hover {
background-color: #008cba;
fill: #FFF;
}
.onTool {
background-color: #008cba;
fill: #FFF;
}
</style>
</head>
<body>
<div class="tools">
<svg id="zoomTool" class="toolIcon onTool" viewBox="0 0 512 512">
<path
d="m289.8,200.2h-49.3v-49.3c0-11.3-9.1-20.4-20.4-20.4-11.3,0-20.4,9.1-20.4,20.4v49.3h-49.3c-11.3,0-20.4,9.1-20.4,20.4 0,11.3 9.1,20.4 20.4,20.4h49.3v49.3c0,11.3 9.1,20.4 20.4,20.4 11.3,0 20.4-9.1 20.4-20.4v-49.3h49.3c11.3,0 20.4-9.1 20.4-20.4 0-11.3-9.2-20.4-20.4-20.4z" />
<path
d="m220.2,388.7c-92.9,0-168.2-75.2-168.2-168.1s75.3-168.1 168.2-168.1 168.1,75.3 168.1,168.1-75.3,168.1-168.1,168.1zm274.8,78l-113.3-113.3c29.7-36.1 47.6-82.4 47.6-132.8 0-115.5-93.6-209.2-209.2-209.2s-209.1,93.7-209.1,209.2 93.6,209.2 209.2,209.2c50.4,0 96.6-17.8 132.7-47.5l113.3,113.3c5.2,5.3 21.1,7.9 28.9,0 7.9-8 7.9-20.9-0.1-28.9z" />
</svg>
<svg id="vectorTool" class="toolIcon" viewBox="0 0 492.426 492.426">
<polygon
points="261.232,434.981 261.232,57.445 305.607,101.82 326.82,80.606 246.213,0 165.607,80.606 186.82,101.82
231.232,57.408 231.232,435.019 186.82,390.606 165.607,411.82 246.213,492.426 326.82,411.82 305.607,390.606 " />
</svg>
<svg id="proportionsTool" class="toolIcon" viewBox="0 0 512 512">
<path d="M504.843,72.607L439.386,7.15c-9.534-9.534-25.048-9.533-34.582,0L281.561,130.392c-3.086,3.087-3.086,8.089,0,11.176
c3.086,3.085,8.089,3.085,11.175,0L415.978,18.326c1.634-1.634,3.806-2.533,6.116-2.533s4.482,0.899,6.116,2.533l65.458,65.457
c1.633,1.633,2.533,3.805,2.533,6.116c0,2.31-0.9,4.482-2.533,6.115l-16.064,16.064L435.515,69.99
c-3.086-3.085-8.089-3.085-11.175,0c-3.086,3.087-3.086,8.089,0,11.176l42.088,42.088l-18.353,18.353l-18.498-18.498
c-3.086-3.085-8.089-3.085-11.175,0c-3.086,3.087-3.086,8.089,0,11.176l18.498,18.497l-18.353,18.353l-18.498-18.498
c-3.086-3.085-8.089-3.085-11.175,0c-3.086,3.087-3.086,8.089,0,11.176l18.498,18.497l-18.353,18.353l-42.089-42.09
c-3.085-3.084-8.089-3.086-11.175,0c-3.086,3.087-3.086,8.089,0,11.176l42.089,42.089l-18.353,18.353l-18.498-18.498
c-3.086-3.085-8.089-3.085-11.175,0c-3.086,3.087-3.086,8.089,0,11.176l18.497,18.497l-15.526,15.526L255.1,179.203l14.45-14.451
c3.086-3.087,3.086-8.089,0-11.176c-3.086-3.085-8.089-3.085-11.175,0l-14.451,14.451L98.156,22.264
C88.018,12.126,74.539,6.543,60.202,6.543c-14.337,0.001-27.816,5.584-37.953,15.722C12.11,32.403,6.527,45.881,6.527,60.219
c0,14.337,5.583,27.816,15.721,37.954l145.765,145.765L7.157,404.797c-9.534,9.535-9.534,25.048,0,34.583l65.457,65.458
C77.232,509.456,83.373,512,89.905,512c6.532,0,12.673-2.544,17.291-7.162L268.054,343.98l11.457,11.457
c3.086,3.085,8.089,3.085,11.175,0c3.086-3.087,3.086-8.089,0-11.176l-15.814-15.814c-0.307-0.486-0.667-0.948-1.09-1.371
c-0.423-0.423-0.885-0.783-1.37-1.09L101.189,154.763l21.191-21.191l13.102,13.102c1.543,1.542,3.565,2.314,5.588,2.314
s4.045-0.772,5.588-2.314c3.086-3.087,3.086-8.089,0-11.176l-13.101-13.101l21.191-21.191l83.579,83.579
c0.003,0.004,0.006,0.007,0.01,0.011c0.004,0.004,0.007,0.006,0.011,0.01l88.081,88.081c0.218,0.288,0.45,0.57,0.713,0.833
s0.544,0.495,0.832,0.713l117.268,117.268l-21.191,21.191L305.735,294.576c-0.071-0.077-0.135-0.158-0.21-0.232l-47.182-47.183
c-0.078-0.078-0.163-0.146-0.243-0.22l-83.343-83.342c-3.086-3.085-8.089-3.085-11.175,0c-3.086,3.087-3.086,8.089,0,11.176
l249.291,249.291l-21.192,21.191l-75.096-75.097c-3.086-3.085-8.089-3.085-11.175,0c-3.086,3.087-3.086,8.089,0,11.176
l80.684,80.684c0.767,0.767,1.717,1.386,2.728,1.778l95.478,37.069c1.506,0.584,3.076,0.868,4.634,0.868
c3.342,0,6.624-1.308,9.073-3.759c3.591-3.593,4.721-8.973,2.88-13.707l-37.108-95.434c-0.392-1.008-1.011-1.96-1.777-2.725
L343.963,268.071l160.88-160.881C514.378,97.655,514.378,82.142,504.843,72.607z M256.882,332.805l-15.507,15.503l-18.498-18.498
c-3.086-3.085-8.089-3.085-11.175,0c-3.086,3.087-3.086,8.089,0,11.176l18.498,18.498l-18.353,18.353l-42.089-42.089
c-3.086-3.085-8.089-3.085-11.175,0c-3.086,3.087-3.086,8.089,0,11.176l42.089,42.089l-18.353,18.353l-18.498-18.498
c-3.086-3.085-8.089-3.085-11.175,0c-3.086,3.087-3.086,8.089,0,11.176l18.498,18.497l-18.353,18.353l-18.498-18.498
c-3.086-3.085-8.089-3.085-11.175,0c-3.086,3.087-3.086,8.089,0,11.176l18.498,18.498l-18.353,18.353l-42.089-42.089
c-3.086-3.085-8.089-3.085-11.175,0c-3.086,3.087-3.086,8.089,0,11.176l42.089,42.088l-16.064,16.064
c-1.634,1.634-3.806,2.534-6.116,2.534s-4.482-0.9-6.116-2.534l-65.457-65.458c-3.372-3.372-3.372-8.859,0-12.231l160.857-160.858
L256.882,332.805z M33.423,86.998c-14.766-14.766-14.766-38.792,0-53.558c14.766-14.767,38.792-14.767,53.558,0l24.976,24.976
l-53.558,53.558L33.423,86.998z M116.798,116.803c-0.002,0.002-0.004,0.003-0.005,0.005c-0.002,0.002-0.003,0.004-0.005,0.005
l-26.774,26.774l-20.439-20.439l53.558-53.558l20.439,20.439L116.798,116.803z M483.703,483.681l-77.864-30.229l47.601-47.601
L483.703,483.681z" />
</svg>
<svg id="colorsTool" class="toolIcon" viewBox="0 0 459 459">
<path
d="M229.5,0C102,0,0,102,0,229.5S102,459,229.5,459c20.4,0,38.25-17.85,38.25-38.25c0-10.2-2.55-17.85-10.2-25.5
c-5.1-7.65-10.2-15.3-10.2-25.5c0-20.4,17.851-38.25,38.25-38.25h45.9c71.4,0,127.5-56.1,127.5-127.5C459,91.8,357,0,229.5,0z
M89.25,229.5c-20.4,0-38.25-17.85-38.25-38.25S68.85,153,89.25,153s38.25,17.85,38.25,38.25S109.65,229.5,89.25,229.5z
M165.75,127.5c-20.4,0-38.25-17.85-38.25-38.25S145.35,51,165.75,51S204,68.85,204,89.25S186.15,127.5,165.75,127.5z
M293.25,127.5c-20.4,0-38.25-17.85-38.25-38.25S272.85,51,293.25,51s38.25,17.85,38.25,38.25S313.65,127.5,293.25,127.5z
M369.75,229.5c-20.4,0-38.25-17.85-38.25-38.25S349.35,153,369.75,153S408,170.85,408,191.25S390.15,229.5,369.75,229.5z" />
</svg>
</div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
<script type="text/javascript" src="lib/blowup.min.js"></script>
<script>
$(document).ready(() => {
// Tool icons objects
const $zoomToolOBJ = $('#zoomTool');
const $vectorToolOBJ = $('#vectorTool');
const $proportionsToolOBJ = $('#proportionsTool');
const $colorsToolOBJ = $('#colorsTool');
// Selected tool -- initiated with zoom tool
let mode = 1;
// Holder for the active tool element (if any)
let $activeToolOBJ = $zoomToolOBJ;
// Handlers for tool icon click event
const zoomToolOnClick = () => {
if (mode !== 1) {
if (!!$activeToolOBJ) {
$activeToolOBJ.removeClass('onTool');
}
$activeToolOBJ = $zoomToolOBJ;
mode = 1;
$zoomToolOBJ.addClass('onTool');
return;
}
mode = 0;
$zoomToolOBJ.removeClass('onTool');
$activeToolOBJ = null;
}
$zoomToolOBJ.on('click', zoomToolOnClick);
});
</script>
</body>
</html>
The problem appears to be with the version of jQuery that you are using. You are using a 2.x version, and this is a jQuery issue for SVG. It works with a 3.x version. Consider upgrading your jQuery version if it is possible. Also see this answer for additional information.
I think the class is being removed but you are adding it straight back on again:
const zoomToolOnClick = () => {
if (mode !== 1) {
if (!!$activeToolOBJ) {
$activeToolOBJ.removeClass('onTool'); // <----- removing class
}
$activeToolOBJ = $zoomToolOBJ;
mode = 1;
console.log($zoomToolOBJ.attr('class'));
$zoomToolOBJ.addClass('onTool'); // <----- adding it back on again
console.log($zoomToolOBJ.attr('class'));
return;
}
mode = 0;
console.log($zoomToolOBJ.attr('class'));
$zoomToolOBJ.removeClass('onTool');
console.log($zoomToolOBJ.attr('class'));
$activeToolOBJ = null;
}
Do you mean to be returning from the function after the class is removed?
I want to make menu navigation bar with several inline-block li elements, each of them must have arrow-like right side. Like this:
I googled for it and the most common answer is to use css tricks with transparent border. Like this one: http://jsfiddle.net/jx99z/5/
html:
<div class="text">Some Text<span class="arrow"></span></div>
css:
.text {
background-color:#ff0000;
color:#fff;
display:inline-block;
padding-left:4px;
}
.arrow {
border-style: dashed;
border-color: transparent;
border-width: 0.20em;
display: -moz-inline-box;
display: inline-block;
/* Use font-size to control the size of the arrow. */
font-size: 100px;
height: 0;
line-height: 0;
position: relative;
vertical-align: middle;
width: 0;
background-color:#fff; /* change background color acc to bg color */
border-left-width: 0.2em;
border-left-style: solid;
border-left-color: #ff0000;
left:0.25em;
}
It seems pretty good, but when I try to add other elements with :hover, they don't look and behave properly: http://jsfiddle.net/txayr2j6/
html:
<div class="text">Some Text<span class="arrow"></span></div>
<div class="text">Some Text<span class="arrow"></span></div>
css:
.text {
background-color:#ff0000;
color:#fff;
display:inline-block;
padding-left:4px;
}
.arrow {
border-style: dashed;
border-color: transparent;
border-width: 0.20em;
display: -moz-inline-box;
display: inline-block;
/* Use font-size to control the size of the arrow. */
font-size: 100px;
height: 0;
line-height: 0;
position: relative;
vertical-align: middle;
width: 0;
background-color:#fff; /* change background color acc to bg color */
border-left-width: 0.2em;
border-left-style: solid;
border-left-color: #ff0000;
left:0.25em;
}
.text:hover {
background-color:#ccc;
border-left-color: #ccc;
}
Another solution, which I found, is that I can draw any element using svg (whatever that means) like this: http://codepen.io/anon/pen/OXWoXd
html:
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<polygon points="
0,0
0,200
270,200
300,100
270,0
150,0
" />
<div>Item 1</div>
</svg>
css:
svg polygon {
fill: transparent;
stroke: black;
stroke-width: 2px;
}
svg {
background-color: #ccc;
height: 50%;
}
body, html {
height: 100%;
margin: .2em;
}
But that solution is even worse: somehow I can't make element wider than 300 px and look at those ugly border and background. Also, I want that bar to be responsive. Thanks!
Svg are great for creating shapes in html
Used a polygon element for the shape.
Text element for the link description.
A element for creating a link.
#arrow-menu a polygon {
fill: #888;
stroke: #222;
}
#arrow-menu a:hover polygon {
stroke: #222;
fill: black;
}
#arrow-menu a:hover text {
fill: cornflowerblue;
}
#arrow-menu a {
font-size: 5px;
}
<svg id="arrow-menu" viewBox="-1 -1 112 22" xmlns:xlink="http://www.w3.org/1999/xlink">
<a xlink:href="#">
<polygon points="0,0 20,0 25,10 20,20 0,20 0,0"></polygon>
<text x="1.5" y="11.5">Menu link</text>
</a>
<a xlink:href="#">
<polygon transform="translate(22)" points="0,0 20,0 25,10 20,20 0,20 5,10 0,0"></polygon>
</a>
<a xlink:href="#">
<polygon transform="translate(44)" points="0,0 20,0 25,10 20,20 0,20 5,10 0,0"></polygon>
</a>
<a xlink:href="#">
<polygon transform="translate(66)" points="0,0 20,0 25,10 20,20 0,20 5,10 0,0"></polygon>
</a>
</svg>
Is it this what you are looking for? It is made with before and after Fiddle
It is better to make it with css than with images
Very interesting question. I believe it is better to use pictures, since you want your hover event to work and also, there is a problem with clicking at the borders. This is a full implementation of the design along with events:
<!DOCTYPE html>
<html>
<head>
<style type="text/css">
.text {
background-color: rgb(237, 28, 36);
color:white;
padding-left:4px;
height: 30px;
padding-top: 10px;
}
</style>
<script type="text/javascript">
function mouseoverText(index) {
var images = document.querySelectorAll(".image");
var currentText = document.querySelectorAll(".text")[index];
currentText.style["background-color"] = "white";
currentText.style["color"] = "red";
images[index].src = ((images.length - 1 === index) ? "white-white" : "white-red") + "-arrow.png";
if (index > 0) {
images[index - 1].src = "red-white-arrow.png";
}
}
function mouseoutText(index) {
var currentText = document.querySelectorAll(".text")[index];
currentText.style["background-color"] = "rgb(237, 28, 36)";
currentText.style["color"] = "white";
var images = document.querySelectorAll(".image");
if (index >= 0) {
images[index].src = ((images.length - 1 === index) ? "red-white" : "red-red") + "-arrow.png";
if (index > 0) {
images[index - 1].src = "red-red-arrow.png";
}
}
}
var lastIndex = -1;
function mouseoverArrow(event, index) {
if (!!event) {
var x = event.offsetX;
var y = event.offsetY;
var height = 40;
if (((y < height / 2) && (x > y)) || ((y >= height / 2) && (x > (height - y)))) {
mouseoverArrow(null, index + 1);
return;
}
}
if (lastIndex !== -1) {
mouseoutArrow();
lastIndex = -1;
}
lastIndex = index;
var texts = document.querySelectorAll(".text");
if (index === texts.length) {
return;
}
texts[index].style["background-color"] = "white";
texts[index].style.color = "red";
mouseoverText(index);
}
function mouseoutArrow() {
if (lastIndex < 0) {
return;
}
var texts = document.querySelectorAll(".text");
if (lastIndex >= texts.length) {
return;
}
texts[lastIndex].style.color = "white";
texts[lastIndex].style["background-color"] = "rgb(237, 28, 36)";
mouseoutText(lastIndex);
lastIndex = -1;
}
function clk(index) {
console.log("Element " + (lastIndex === -1 ? index : lastIndex) + " was clicked");
}
</script>
</head>
<body>
<div style="display: inline-flex;">
<div class="text" onmousemove="mouseoverText(0);" onmouseout="mouseoutText(0);" onclick="clk(0);">Some Text</div>
<img class="image" src="red-red-arrow.png" onmousemove="mouseoverArrow(event, 0);" onmouseout="mouseoutArrow();" onclick="clk();" />
<div class="text" onmousemove="mouseoverText(1);" onmouseout="mouseoutText(1);" onclick="clk(1);">Some Text</div>
<img class="image" src="red-red-arrow.png" onmousemove="mouseoverArrow(event, 1);" onmouseout="mouseoutArrow();" onclick="clk();" />
<div class="text" onmousemove="mouseoverText(2);" onmouseout="mouseoutText(2);" onclick="clk(2);">Some Text</div>
<img class="image" src="red-white-arrow.png" onmousemove="mouseoverArrow(event, 2);" onmouseout="mouseoutArrow();" onclick="clk();" />
</div>
</body>
</html>
I attach the useful pictures as well: